Colin Watson has proposed merging lp:~cjwatson/launchpad/remove-branch-set-api into lp:launchpad.
Commit message: Remove BranchSetAPI. Since the calling code in bzr relies on password authentication, it can't have worked since at least 2012. Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~cjwatson/launchpad/remove-branch-set-api/+merge/324474 https://code.launchpad.net/~cjwatson/bzr/remove-register-branch/+merge/324472 removes the bzr side. Ordinarily we'd probably need to go through a deprecation cycle or something, but since this API has been unusable for years we've effectively done so already, albeit by accident. -- Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/remove-branch-set-api into lp:launchpad.
=== modified file 'lib/lp/code/configure.zcml' --- lib/lp/code/configure.zcml 2017-05-04 16:02:40 +0000 +++ lib/lp/code/configure.zcml 2017-05-23 12:56:33 +0000 @@ -182,11 +182,6 @@ <xmlrpc:view for="lp.code.interfaces.codehosting.IBazaarApplication" - interface="lp.code.xmlrpc.branch.IBranchSetAPI" - class="lp.code.xmlrpc.branch.BranchSetAPI" - permission="launchpad.AnyPerson"/> - <xmlrpc:view - for="lp.code.interfaces.codehosting.IBazaarApplication" interface="lp.code.xmlrpc.branch.IPublicCodehostingAPI" class="lp.code.xmlrpc.branch.PublicCodehostingAPI" permission="zope.Public"/> === modified file 'lib/lp/code/doc/branch-xmlrpc.txt' --- lib/lp/code/doc/branch-xmlrpc.txt 2016-02-04 04:59:12 +0000 +++ lib/lp/code/doc/branch-xmlrpc.txt 2017-05-23 12:56:33 +0000 @@ -1,310 +1,8 @@ -IBranchSetAPI -============= +IPublicCodehostingAPI +===================== - >>> from zope.security.proxy import removeSecurityProxy - >>> from datetime import datetime - >>> import pytz >>> import xmlrpclib >>> from lp.testing.xmlrpc import XMLRPCTestTransport - >>> branchset_api = xmlrpclib.ServerProxy( - ... 'http://foo....@canonical.com:t...@xmlrpc.launchpad.dev/bazaar/', - ... transport=XMLRPCTestTransport()) - - -register_branch -=============== - -register_branch(branch_url, branch_name, branch_title, - branch_description, author_email, product_name, - owner_name='') - -Let's register a branch using register_branch(). First we make sure that -there is no branch with the URL we are going to register. - - >>> from lp.code.interfaces.branchlookup import IBranchLookup - >>> branch_url = 'http://foo.com/branch' - >>> getUtility(IBranchLookup).getByUrl(branch_url) is None - True - -The only thing that we have to supply is the branch url. The rest is -optional, so we pass '' for the rest of the parameters. The reason we -pass '' is that XMLRPC doesn't allow optional parameters, and None is an -extension to XMLRPC which we don't want to rely on. We use the currently -authenticated user as the owner of the branch. - - >>> branchset_api.register_branch( - ... 'http://foo.com/branch', '', '', '', '', '') - 'http://code.launchpad.dev/~name16/+junk/branch' - -Now we can see that a branch got registered: - - >>> new_branch = getUtility(IBranchLookup).getByUrl(branch_url) - -The author_email is now ignored as the author attribute has been -removed from the branch object. - - >>> print new_branch.owner.displayname - Foo Bar - -The name got set to the last component of the URL since it wasn't -specified: - - >>> print new_branch.name - branch - -Since we didn't specify product, title or description, they are None: - - >>> new_branch.product is None - True - -Also, Launchpad is scheduled to mirror this branch on its next mirror run: - - >>> removeSecurityProxy(new_branch).sync() - >>> print new_branch.next_mirror_time is not None - True - >>> print new_branch.next_mirror_time < datetime.now(pytz.timezone('UTC')) - True - -(We remove the security proxy in order to ensure that next_mirror_time has a -real object in it, rather than an expression builder.) - -Let's register a branch specifying the name, description and product as -well: - - >>> other_branch_url = 'http://foo.com/other_branch' - >>> branchset_api.register_branch( - ... other_branch_url, 'branch_name', 'branch_title', - ... 'branch description', 't...@canonical.com', 'evolution') - 'http://code.launchpad.dev/~name16/evolution/branch_name' - - >>> new_branch = getUtility(IBranchLookup).getByUrl(other_branch_url) - >>> print new_branch.owner.displayname - Foo Bar - - >>> print new_branch.name - branch_name - >>> print new_branch.product.displayname - Evolution - -If we try to register a branch with a branch name that is already used: - - >>> yet_another_branch_url = 'http://foo.com/yet_another_branch' - >>> branchset_api.register_branch( - ... yet_another_branch_url, 'branch_name', 'branch_title', - ... 'branch description', 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 220: 'Branch name already in use: - A branch with the name "branch_name" already exists - for name16 in evolution.'> - -However a branch can be registered with a name used by a different user. - - >>> evo_main_url = 'http://foo.example.com/evo/main' - >>> branchset_api.register_branch( - ... evo_main_url, 'main', 'branch_title', - ... 'branch description', 't...@canonical.com', 'evolution') - 'http://code.launchpad.dev/~name16/evolution/main' - -This check must also detect branch name conflict for +junk branches: - - >>> branchset_api.register_branch( - ... yet_another_branch_url, 'branch', '', '', '', '') - Traceback (most recent call last): - ... - Fault: <Fault 220: 'Branch name already in use: - A junk branch with the name "branch" already exists for name16.'> - -If we give a product name that doesn't exist, we get an error: - - >>> new_branch_url = 'http://foo.com/new_branch' - >>> branchset_api.register_branch( - ... new_branch_url, 'branch_name', 'branch_title', - ... 'branch description', 't...@canonical.com', 'non-existing') - Traceback (most recent call last): - ... - Fault: <Fault 10: 'No such project: non-existing'> - -If we try to register a branch with an invalid URI: - - >>> invalid_uri = '.' - >>> branchset_api.register_branch( - ... invalid_uri, 'branch_name', 'branch title', 'branch description', - ... 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 120: 'Invalid URL: .\n"." is not a valid URI'> - -If we try to register a URL with a unsupported scheme: - - >>> bad_scheme = 'svn://chinstrap.ubuntu.com/foo/bar/baz' - >>> branchset_api.register_branch( - ... bad_scheme, 'branch_name', 'branch title', 'branch description', - ... 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 120: 'Invalid URL: - svn://chinstrap.ubuntu.com/foo/bar/baz\nThe URI scheme - "svn" is not allowed. Only URIs with the following - schemes may be used: bzr+ssh, ftp, http, https, sftp'> - -If we try to register a root URL: - - >>> root_url = 'http://example.com/' - >>> branchset_api.register_branch( - ... root_url, 'branch_name', 'branch title', 'branch description', - ... 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 120: 'Invalid URL: http://example.com\nURLs for branches - cannot point to the root of a site.'> - -If we try to register a URL in launchpad.net (actually, launchpad.dev because -we are in the testing environment): - - >>> launchpad_url = 'https://blueprints.launchpad.dev/foo/bar/baz' - >>> branchset_api.register_branch( - ... launchpad_url, 'branch_name', 'branch title', - ... 'branch description', 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 120: - 'Invalid URL: https://blueprints.launchpad.dev/foo/bar/baz\nFor - Launchpad to mirror a branch, the original branch cannot be on - <code>launchpad.dev</code>.'> - -Registering a branch name that ends in a slash works, but the slash -gets removed from the stored URL: - - >>> branchset_api.register_branch( - ... new_branch_url + '/', 'new_branch', 'branch_title', - ... 'branch_description', 't...@canonical.com', 'evolution') - 'http://code.launchpad.dev/~name16/evolution/new_branch' - - >>> new_branch = getUtility(IBranchLookup).getByUrl(new_branch_url) - >>> print new_branch.url - http://foo.com/new_branch - >>> print new_branch.owner.name - name16 - >>> print new_branch.product.name - evolution - >>> print new_branch.name - new_branch - - -If we try to register a branch which is already registered in Launchpad: - - >>> getUtility(IBranchLookup).getByUrl(other_branch_url) is not None - True - >>> branchset_api.register_branch( - ... other_branch_url, 'branch_name', 'branch_title', - ... 'branch description', 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 50: 'http://foo.com/other_branch is already registered.'> - - -We get the same error if we try to register the branch with a slash appended: - >>> branchset_api.register_branch( - ... other_branch_url + '/', 'branch_name', 'branch_title', - ... 'branch description', 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 50: 'http://foo.com/other_branch is already registered.'> - -Attempting to register a branch for a product where the branch visibility -policy is forbidden will cause a fault result. - - >>> test_user_api = xmlrpclib.ServerProxy( - ... 'http://no-p...@canonical.com:t...@xmlrpc.launchpad.dev/bazaar/', - ... transport=XMLRPCTestTransport()) - >>> test_user_api.register_branch( - ... 'http://foo.com/bar', 'branch_name', 'branch_title', - ... 'branch description', 'no-p...@canonical.com', 'landscape') - Traceback (most recent call last): - ... - Fault: <Fault 110: 'You are not allowed to create a branch for project: - The Landscape Project'> - -We can register a branch as being owned by a team, rather than just an -individual: - - >>> branchset_api.register_branch( - ... 'http://example.com/unregistered-as-yet', 'team-branch', - ... 'new-title', 'new-description', 't...@canonical.com', - ... 'evolution', 'name18') - 'http://code.launchpad.dev/~name18/evolution/team-branch' - -If the team doesn't exist, we raise a Fault that says so: - - >>> branchset_api.register_branch( - ... 'http://example.com/team-dont-exist', 'team-dont-exist', - ... 'title', 'description', 't...@canonical.com', 'evolution', - ... 'no-existy-teamo') - Traceback (most recent call last): - ... - Fault: <Fault 200: 'No such person or team: no-existy-teamo'> - -We can only register branches as belonging to a team that we are a -member of. Doing otherwise raises a Fault: - - >>> branchset_api.register_branch( - ... 'http://example.com/invalid-team', 'invalid-team', 'title', - ... 'description', 't...@canonical.com', 'evolution', - ... 'landscape-developers') - Traceback (most recent call last): - ... - Fault: <Fault 250: 'name16 is not a member of landscape-developers.'> - -Trying to register a branch with an invalid name will fail with a fault that -explains the problem: - - >>> branchset_api.register_branch( - ... 'http://example.com/invalid-branch-name', '.bzr', 'title', 'desc', - ... 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 260: 'Invalid branch name '.bzr'. Branch names must ... - -Crazy unicode characters are also invalid: - - >>> branchset_api.register_branch( - ... 'http://example.com/invalid-branch-name', - ... u'\N{latin small letter e with acute}', 'title', 'desc', - ... 't...@canonical.com', 'evolution') - Traceback (most recent call last): - ... - Fault: <Fault 260: u'Invalid branch name '\xe9'. Branch names must ... - - -link_branch_to_bug(branch_url, bug_id) -====================================== - -link_branch_to_bug() associates a branch with a bug. Let's associate -the branch we registered with bug 1. If the link is successful, the bug -URL is returned. - - >>> branchset_api.link_branch_to_bug(other_branch_url, '1') - 'http://bugs.launchpad.dev/bugs/1' - -Let's take a look to see that the branch actually got linked to the bug: - - >>> from lp.bugs.interfaces.bug import IBugSet - >>> bug_one = getUtility(IBugSet).get(1) - >>> for branch in bug_one.linked_branches: - ... print branch.url - http://foo.com/other_branch - -We get an error if we try to specify a non-existant branch or bug: - - >>> branchset_api.link_branch_to_bug('http://foo.com/unknown', '1') - Traceback (most recent call last): - ... - Fault: <Fault 30: 'No such branch: http://foo.com/unknown'> - >>> branchset_api.link_branch_to_bug(branch_url, '99') - Traceback (most recent call last): - ... - Fault: <Fault 40: 'No such bug: 99'> resolve_lp_path @@ -319,9 +17,7 @@ This API is deprecated, and will eventually be replaced with an equivalent method in the new Launchpad API infrastructure. -Note that authentication is not required to use this method. - - >>> branchset_api = xmlrpclib.ServerProxy( + >>> public_codehosting_api = xmlrpclib.ServerProxy( ... 'http://xmlrpc.launchpad.dev/bazaar/', ... transport=XMLRPCTestTransport()) @@ -329,7 +25,8 @@ On success, resolve_lp_path returns a dict containing a single key, 'urls': - >>> results = branchset_api.resolve_lp_path('~vcs-imports/evolution/main') + >>> results = public_codehosting_api.resolve_lp_path( + ... '~vcs-imports/evolution/main') >>> print results.keys() ['urls'] @@ -337,7 +34,8 @@ The value of a key is a list of URLs from which the branch can be accessed: - >>> results = branchset_api.resolve_lp_path('~vcs-imports/evolution/main') + >>> results = public_codehosting_api.resolve_lp_path( + ... '~vcs-imports/evolution/main') >>> for url in results['urls']: ... print url bzr+ssh://bazaar.launchpad.dev/~vcs-imports/evolution/main === modified file 'lib/lp/code/model/branchnamespace.py' --- lib/lp/code/model/branchnamespace.py 2015-10-09 15:49:14 +0000 +++ lib/lp/code/model/branchnamespace.py 2017-05-23 12:56:33 +0000 @@ -1,4 +1,4 @@ -# Copyright 2009-2015 Canonical Ltd. This software is licensed under the +# Copyright 2009-2017 Canonical Ltd. This software is licensed under the # GNU Affero General Public License version 3 (see the file LICENSE). """Implementations of `IBranchNamespace`.""" @@ -199,10 +199,10 @@ raise BranchExists(existing_branch) # Not all code paths that lead to branch creation go via a - # schema-validated form (e.g. the register_branch XML-RPC call or - # pushing a new branch to codehosting), so we validate the branch name - # here to give a nicer error message than 'ERROR: new row for relation - # "branch" violates check constraint "valid_name"...'. + # schema-validated form (e.g. pushing a new branch to codehosting), + # so we validate the branch name here to give a nicer error message + # than 'ERROR: new row for relation "branch" violates check + # constraint "valid_name"...'. IBranch['name'].validate(unicode(name)) def validateMove(self, branch, mover, name=None): === modified file 'lib/lp/code/xmlrpc/branch.py' --- lib/lp/code/xmlrpc/branch.py 2015-09-28 17:38:45 +0000 +++ lib/lp/code/xmlrpc/branch.py 2017-05-23 12:56:33 +0000 @@ -1,12 +1,10 @@ -# Copyright 2009-2012 Canonical Ltd. This software is licensed under the +# Copyright 2009-2017 Canonical Ltd. This software is licensed under the # GNU Affero General Public License version 3 (see the file LICENSE). """Branch XMLRPC API.""" __metaclass__ = type __all__ = [ - 'BranchSetAPI', - 'IBranchSetAPI', 'IPublicCodehostingAPI', 'PublicCodehostingAPI', ] @@ -21,21 +19,14 @@ Interface, ) -from lp.app.errors import NotFoundError -from lp.app.validators import LaunchpadValidationError -from lp.bugs.interfaces.bug import IBugSet from lp.code.enums import BranchType from lp.code.errors import ( - BranchCreationException, - BranchCreationForbidden, CannotHaveLinkedBranch, InvalidNamespace, NoLinkedBranch, NoSuchBranch, ) -from lp.code.interfaces.branch import IBranch from lp.code.interfaces.branchlookup import IBranchLookup -from lp.code.interfaces.branchnamespace import get_branch_namespace from lp.code.interfaces.codehosting import ( BRANCH_ALIAS_PREFIX, compose_public_url, @@ -45,134 +36,18 @@ NoSuchDistroSeries, NoSuchSourcePackageName, ) -from lp.registry.interfaces.person import ( - IPersonSet, - NoSuchPerson, - ) +from lp.registry.interfaces.person import NoSuchPerson from lp.registry.interfaces.product import ( InvalidProductName, - IProductSet, NoSuchProduct, ) from lp.registry.interfaces.productseries import NoSuchProductSeries from lp.services.config import config -from lp.services.webapp import ( - canonical_url, - LaunchpadXMLRPCView, - ) -from lp.services.webapp.interfaces import ILaunchBag +from lp.services.webapp import LaunchpadXMLRPCView from lp.xmlrpc import faults from lp.xmlrpc.helpers import return_fault -class IBranchSetAPI(Interface): - """An XMLRPC interface for dealing with branches. - - This XML-RPC interface was introduced to support Bazaar 0.8-2, which is - included in Ubuntu 6.06. This interface cannot be removed until Ubuntu - 6.06 is end-of-lifed. - """ - - def register_branch(branch_url, branch_name, branch_title, - branch_description, author_email, product_name, - owner_name=''): - """Register a new branch in Launchpad.""" - - def link_branch_to_bug(branch_url, bug_id): - """Link the branch to the bug.""" - - -@implementer(IBranchSetAPI) -class BranchSetAPI(LaunchpadXMLRPCView): - - def register_branch(self, branch_url, branch_name, branch_title, - branch_description, author_email, product_name, - owner_name=''): - """See IBranchSetAPI.""" - registrant = getUtility(ILaunchBag).user - assert registrant is not None, ( - "register_branch shouldn't be accessible to unauthenicated" - " requests.") - - person_set = getUtility(IPersonSet) - if owner_name: - owner = person_set.getByName(owner_name) - if owner is None: - return faults.NoSuchPersonWithName(owner_name) - if not registrant.inTeam(owner): - return faults.NotInTeam(registrant.name, owner_name) - else: - owner = registrant - - if product_name: - product = getUtility(IProductSet).getByName(product_name) - if product is None: - return faults.NoSuchProduct(product_name) - else: - product = None - - # Branch URLs in Launchpad do not end in a slash, so strip any - # slashes from the end of the URL. - branch_url = branch_url.rstrip('/') - - branch_lookup = getUtility(IBranchLookup) - existing_branch = branch_lookup.getByUrl(branch_url) - if existing_branch is not None: - return faults.BranchAlreadyRegistered(branch_url) - - try: - unicode_branch_url = branch_url.decode('utf-8') - IBranch['url'].validate(unicode_branch_url) - except LaunchpadValidationError as exc: - return faults.InvalidBranchUrl(branch_url, exc) - - # We want it to be None in the database, not ''. - if not branch_description: - branch_description = None - if not branch_title: - branch_title = None - - if not branch_name: - branch_name = unicode_branch_url.split('/')[-1] - - try: - if branch_url: - branch_type = BranchType.MIRRORED - else: - branch_type = BranchType.HOSTED - namespace = get_branch_namespace(owner, product) - branch = namespace.createBranch( - branch_type=branch_type, - name=branch_name, registrant=registrant, - url=branch_url, title=branch_title, - summary=branch_description) - if branch_type == BranchType.MIRRORED: - branch.requestMirror() - except BranchCreationForbidden: - return faults.BranchCreationForbidden(product.displayname) - except BranchCreationException as err: - return faults.BranchNameInUse(err) - except LaunchpadValidationError as err: - return faults.InvalidBranchName(err) - - return canonical_url(branch) - - def link_branch_to_bug(self, branch_url, bug_id): - """See IBranchSetAPI.""" - branch = getUtility(IBranchLookup).getByUrl(url=branch_url) - if branch is None: - return faults.NoSuchBranch(branch_url) - try: - bug = getUtility(IBugSet).get(bug_id) - except NotFoundError: - return faults.NoSuchBug(bug_id) - # Since this API is controlled using launchpad.AnyPerson there must be - # an authenticated person, so use this person as the registrant. - registrant = getUtility(ILaunchBag).user - bug.linkBranch(branch, registrant=registrant) - return canonical_url(bug) - - class IPublicCodehostingAPI(Interface): """The public codehosting API."""
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp