8687.15.17
by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/. |
1 |
# Copyright 2009 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
6789.5.1
by Michael Hudson
136 lines of joy to add a 'branchdetails' endpoint to the internal xml-rpc |
3 |
|
6789.8.7
by Michael Hudson
rename branchdetails endpoint to puller_api. |
4 |
"""Implementations of the XML-RPC APIs for codehosting."""
|
6789.5.1
by Michael Hudson
136 lines of joy to add a 'branchdetails' endpoint to the internal xml-rpc |
5 |
|
6 |
__metaclass__ = type |
|
7 |
__all__ = [ |
|
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
8 |
'CodehostingAPI', |
7055.4.4
by Jonathan Lange
Move the fakes out of the test module. |
9 |
'datetime_from_tuple', |
6789.5.1
by Michael Hudson
136 lines of joy to add a 'branchdetails' endpoint to the internal xml-rpc |
10 |
]
|
11 |
||
6789.6.19
by Michael Hudson
copy and make slightly less general run_as_requester into xmlrpc code, together |
12 |
|
6789.6.10
by Jonathan Lange
Make recordSuccess tests pass. |
13 |
import datetime |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
14 |
|
15 |
from bzrlib.urlutils import ( |
|
16 |
escape, |
|
17 |
unescape, |
|
18 |
)
|
|
19 |
import pytz |
|
11121.5.38
by Tim Penhey
Make sure that the branch doesn't exist after a link failure. |
20 |
import transaction |
6789.6.2
by Michael Hudson
copy-paste tests, adapt and make pass the startMirroring tests |
21 |
from zope.component import getUtility |
22 |
from zope.interface import implements |
|
6789.6.26
by Jonathan Lange
getBranchInformation tests, interface and implementation. |
23 |
from zope.security.interfaces import Unauthorized |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
24 |
from zope.security.management import endInteraction |
6789.6.2
by Michael Hudson
copy-paste tests, adapt and make pass the startMirroring tests |
25 |
from zope.security.proxy import removeSecurityProxy |
26 |
||
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
27 |
from lp.app.errors import ( |
28 |
NameLookupFailed, |
|
29 |
NotFoundError, |
|
30 |
)
|
|
12442.2.9
by j.c.sackett
Ran import reformatter per review. |
31 |
from lp.app.validators import LaunchpadValidationError |
11270.2.12
by Tim Penhey
Fix and reorder imports. |
32 |
from lp.code.enums import BranchType |
11121.5.21
by Tim Penhey
Merge and resolve conflicts. |
33 |
from lp.code.errors import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
34 |
BranchCreationException, |
11474.5.16
by Tim Penhey
Test invalid product name. |
35 |
CannotHaveLinkedBranch, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
36 |
InvalidNamespace, |
37 |
NoLinkedBranch, |
|
38 |
UnknownBranchTypeError, |
|
39 |
)
|
|
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
40 |
from lp.code.interfaces import branchpuller |
13084.5.3
by Aaron Bentley
extract get_db_branch_info from CodehostingAPI.branchChanged. |
41 |
from lp.code.interfaces.branch import get_db_branch_info |
11121.5.24
by Tim Penhey
slow progress |
42 |
from lp.code.interfaces.branchlookup import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
43 |
IBranchLookup, |
44 |
ILinkedBranchTraverser, |
|
45 |
)
|
|
8138.1.2
by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing. |
46 |
from lp.code.interfaces.branchnamespace import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
47 |
lookup_branch_namespace, |
48 |
split_unique_name, |
|
49 |
)
|
|
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
50 |
from lp.code.interfaces.branchtarget import IBranchTarget |
8138.1.2
by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing. |
51 |
from lp.code.interfaces.codehosting import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
52 |
BRANCH_ALIAS_PREFIX, |
12707.7.6
by Tim Penhey
Created a function to do the basic alias assembly. |
53 |
branch_id_alias, |
12707.4.6
by Tim Penhey
db version of translatePath seems to work, now for the inmemory copy. |
54 |
BRANCH_ID_ALIAS_PREFIX, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
55 |
BRANCH_TRANSPORT, |
56 |
CONTROL_TRANSPORT, |
|
57 |
ICodehostingAPI, |
|
58 |
LAUNCHPAD_ANONYMOUS, |
|
59 |
LAUNCHPAD_SERVICES, |
|
60 |
)
|
|
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
61 |
from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch |
13662.5.3
by Aaron Bentley
Handle invalid source package names. |
62 |
from lp.registry.errors import ( |
63 |
InvalidName, |
|
64 |
NoSuchSourcePackageName, |
|
65 |
)
|
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
66 |
from lp.registry.interfaces.person import ( |
67 |
IPersonSet, |
|
68 |
NoSuchPerson, |
|
69 |
)
|
|
11474.5.16
by Tim Penhey
Test invalid product name. |
70 |
from lp.registry.interfaces.product import ( |
71 |
InvalidProductName, |
|
72 |
NoSuchProduct, |
|
73 |
)
|
|
13662.5.1
by Aaron Bentley
Pushing to non-existant sourcepackagename creates. |
74 |
from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet |
8356.1.1
by Leonard Richardson
Partial move. |
75 |
from lp.services.scripts.interfaces.scriptactivity import IScriptActivitySet |
9719.2.1
by Michael Hudson
move iter_split somewhere generic |
76 |
from lp.services.utils import iter_split |
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
77 |
from lp.services.webapp import LaunchpadXMLRPCView |
78 |
from lp.services.webapp.authorization import check_permission |
|
79 |
from lp.services.webapp.interaction import setupInteractionForPerson |
|
80 |
from lp.xmlrpc import faults |
|
81 |
from lp.xmlrpc.helpers import return_fault |
|
6789.5.1
by Michael Hudson
136 lines of joy to add a 'branchdetails' endpoint to the internal xml-rpc |
82 |
|
83 |
||
6789.6.10
by Jonathan Lange
Make recordSuccess tests pass. |
84 |
UTC = pytz.timezone('UTC') |
85 |
||
86 |
||
9590.1.48
by Michael Hudson
a start at combining the puller and filesystem endpoints |
87 |
def datetime_from_tuple(time_tuple): |
88 |
"""Create a datetime from a sequence that quacks like time.struct_time.
|
|
89 |
||
90 |
The tm_isdst is (index 8) is ignored. The created datetime uses
|
|
91 |
tzinfo=UTC.
|
|
92 |
"""
|
|
93 |
[year, month, day, hour, minute, second, unused, unused, unused] = ( |
|
94 |
time_tuple) |
|
95 |
return datetime.datetime( |
|
96 |
year, month, day, hour, minute, second, tzinfo=UTC) |
|
97 |
||
98 |
||
99 |
def run_with_login(login_id, function, *args, **kwargs): |
|
100 |
"""Run 'function' logged in with 'login_id'.
|
|
101 |
||
102 |
The first argument passed to 'function' will be the Launchpad
|
|
103 |
`Person` object corresponding to 'login_id'.
|
|
104 |
||
105 |
The exception is when the requesting login ID is `LAUNCHPAD_SERVICES`. In
|
|
106 |
that case, we'll pass through the `LAUNCHPAD_SERVICES` variable and the
|
|
107 |
method will do whatever security proxy hackery is required to provide read
|
|
108 |
privileges to the Launchpad services.
|
|
109 |
"""
|
|
110 |
if login_id == LAUNCHPAD_SERVICES or login_id == LAUNCHPAD_ANONYMOUS: |
|
111 |
# Don't pass in an actual user. Instead pass in LAUNCHPAD_SERVICES
|
|
112 |
# and expect `function` to use `removeSecurityProxy` or similar.
|
|
113 |
return function(login_id, *args, **kwargs) |
|
114 |
if isinstance(login_id, basestring): |
|
115 |
requester = getUtility(IPersonSet).getByName(login_id) |
|
116 |
else: |
|
117 |
requester = getUtility(IPersonSet).get(login_id) |
|
118 |
if requester is None: |
|
119 |
raise NotFoundError("No person with id %s." % login_id) |
|
9590.1.51
by Michael Hudson
merge trunk via previous threads, fixing conflicts |
120 |
setupInteractionForPerson(requester) |
9590.1.48
by Michael Hudson
a start at combining the puller and filesystem endpoints |
121 |
try: |
122 |
return function(requester, *args, **kwargs) |
|
123 |
finally: |
|
9590.1.51
by Michael Hudson
merge trunk via previous threads, fixing conflicts |
124 |
endInteraction() |
9590.1.48
by Michael Hudson
a start at combining the puller and filesystem endpoints |
125 |
|
126 |
||
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
127 |
class CodehostingAPI(LaunchpadXMLRPCView): |
128 |
"""See `ICodehostingAPI`."""
|
|
9590.1.48
by Michael Hudson
a start at combining the puller and filesystem endpoints |
129 |
|
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
130 |
implements(ICodehostingAPI) |
6789.5.1
by Michael Hudson
136 lines of joy to add a 'branchdetails' endpoint to the internal xml-rpc |
131 |
|
10379.2.5
by Michael Hudson
integration test and implied fixes |
132 |
def acquireBranchToPull(self, branch_type_names): |
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
133 |
"""See `ICodehostingAPI`."""
|
10379.2.3
by Michael Hudson
acquireBranchToPull changes |
134 |
branch_types = [] |
135 |
for branch_type_name in branch_type_names: |
|
136 |
try: |
|
137 |
branch_types.append(BranchType.items[branch_type_name]) |
|
138 |
except KeyError: |
|
139 |
raise UnknownBranchTypeError( |
|
140 |
'Unknown branch type: %r' % (branch_type_name,)) |
|
9590.1.53
by Michael Hudson
fix couple more mis-merges and an earlier mistake |
141 |
branch = getUtility(branchpuller.IBranchPuller).acquireBranchToPull( |
10379.2.3
by Michael Hudson
acquireBranchToPull changes |
142 |
*branch_types) |
8312.3.5
by Michael Hudson
mostly copy and pasted tests for the xml-rpc endpoint |
143 |
if branch is not None: |
8811.1.1
by Michael Hudson
assorted tests and tweaks |
144 |
branch = removeSecurityProxy(branch) |
8312.3.8
by Michael Hudson
return the branch type from the xml-rpc method too |
145 |
default_branch = branch.target.default_stacked_on_branch |
8721.2.3
by Michael Hudson
bit less broken |
146 |
if default_branch is None: |
147 |
default_branch_name = '' |
|
148 |
elif (branch.branch_type == BranchType.MIRRORED |
|
149 |
and default_branch.private): |
|
150 |
default_branch_name = '' |
|
8312.3.8
by Michael Hudson
return the branch type from the xml-rpc method too |
151 |
else: |
8721.2.3
by Michael Hudson
bit less broken |
152 |
default_branch_name = '/' + default_branch.unique_name |
8312.3.8
by Michael Hudson
return the branch type from the xml-rpc method too |
153 |
return (branch.id, branch.getPullURL(), branch.unique_name, |
154 |
default_branch_name, branch.branch_type.name) |
|
8312.3.5
by Michael Hudson
mostly copy and pasted tests for the xml-rpc endpoint |
155 |
else: |
156 |
return () |
|
157 |
||
6789.6.11
by Jonathan Lange
Make the XML-RPC parameters coding standard compliant. |
158 |
def mirrorFailed(self, branch_id, reason): |
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
159 |
"""See `ICodehostingAPI`."""
|
7940.2.5
by Jonathan Lange
Move branch lookup methods to IBranchLookup |
160 |
branch = getUtility(IBranchLookup).get(branch_id) |
6789.6.5
by Jonathan Lange
Make mirrorFailed tests pass. |
161 |
if branch is None: |
6125.37.7
by Jonathan Lange
Make the rest of the methods return NoBranchWithID if there's no branch |
162 |
return faults.NoBranchWithID(branch_id) |
9590.1.76
by Michael Hudson
remove startMirroring from the endpoint too |
163 |
# The puller runs as no user and may pull private branches. We need to
|
164 |
# bypass Zope's security proxy to set the mirroring information.
|
|
6789.6.5
by Jonathan Lange
Make mirrorFailed tests pass. |
165 |
removeSecurityProxy(branch).mirrorFailed(reason) |
166 |
return True |
|
167 |
||
6789.6.10
by Jonathan Lange
Make recordSuccess tests pass. |
168 |
def recordSuccess(self, name, hostname, started_tuple, completed_tuple): |
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
169 |
"""See `ICodehostingAPI`."""
|
6789.6.10
by Jonathan Lange
Make recordSuccess tests pass. |
170 |
date_started = datetime_from_tuple(started_tuple) |
171 |
date_completed = datetime_from_tuple(completed_tuple) |
|
172 |
getUtility(IScriptActivitySet).recordSuccess( |
|
173 |
name=name, date_started=date_started, |
|
174 |
date_completed=date_completed, hostname=hostname) |
|
175 |
return True |
|
176 |
||
11121.5.13
by Tim Penhey
Function defn. |
177 |
def _getBranchNamespaceExtras(self, path, requester): |
178 |
"""Get the branch namespace, branch name and callback for the path.
|
|
179 |
||
180 |
If the path defines a full branch path including the owner and branch
|
|
181 |
name, then the namespace that is returned is the namespace for the
|
|
182 |
owner and the branch target specified.
|
|
183 |
||
184 |
If the path uses an lp short name, then we only allow the requester to
|
|
185 |
create a branch if they have permission to link the newly created
|
|
186 |
branch to the short name target. If there is an existing branch
|
|
187 |
already linked, then BranchExists is raised. The branch name that is
|
|
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
188 |
used is determined by the namespace as the first unused name starting
|
189 |
with 'trunk'.
|
|
11121.5.13
by Tim Penhey
Function defn. |
190 |
"""
|
11121.5.20
by Tim Penhey
Framework in place. |
191 |
if path.startswith(BRANCH_ALIAS_PREFIX + '/'): |
192 |
path = path[len(BRANCH_ALIAS_PREFIX) + 1:] |
|
193 |
if not path.startswith('~'): |
|
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
194 |
context = getUtility(ILinkedBranchTraverser).traverse(path) |
195 |
target = IBranchTarget(context) |
|
196 |
namespace = target.getNamespace(requester) |
|
197 |
branch_name = namespace.findUnusedName('trunk') |
|
13084.5.3
by Aaron Bentley
extract get_db_branch_info from CodehostingAPI.branchChanged. |
198 |
|
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
199 |
def link_func(new_branch): |
200 |
link = ICanHasLinkedBranch(context) |
|
201 |
link.setBranch(new_branch, requester) |
|
11121.5.28
by Tim Penhey
Add tests for product series, inmemory tests still failing. |
202 |
return namespace, branch_name, link_func, path |
11121.5.20
by Tim Penhey
Framework in place. |
203 |
namespace_name, branch_name = split_unique_name(path) |
204 |
namespace = lookup_branch_namespace(namespace_name) |
|
11121.5.28
by Tim Penhey
Add tests for product series, inmemory tests still failing. |
205 |
return namespace, branch_name, None, path |
11121.5.20
by Tim Penhey
Framework in place. |
206 |
|
7055.10.28
by Jonathan Lange
Have createBranch take a fragment and parse it, rather than parameters. |
207 |
def createBranch(self, login_id, branch_path): |
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
208 |
"""See `ICodehostingAPI`."""
|
6789.9.1
by Michael Hudson
Turns out that Zope won't let you have decorators on XML-RPC methods. Or rather, |
209 |
def create_branch(requester): |
7213.4.6
by Jonathan Lange
Require createBranch to take an absolute path, bringing it inline |
210 |
if not branch_path.startswith('/'): |
211 |
return faults.InvalidPath(branch_path) |
|
9738.1.4
by Michael Hudson
a small collection of hacks to get creating a non-ascii branch name failing with a good message |
212 |
escaped_path = unescape(branch_path.strip('/')) |
7213.4.4
by Jonathan Lange
createBranch now raises PermissionDenied when users try to create branches |
213 |
try: |
11121.5.28
by Tim Penhey
Add tests for product series, inmemory tests still failing. |
214 |
namespace, branch_name, link_func, path = ( |
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
215 |
self._getBranchNamespaceExtras(escaped_path, requester)) |
7213.4.4
by Jonathan Lange
createBranch now raises PermissionDenied when users try to create branches |
216 |
except ValueError: |
7459.6.5
by Michael Hudson
kill PERMISSION_DENIED_FAULT_CODE, replace with faults.PermissionDenied |
217 |
return faults.PermissionDenied( |
7213.4.4
by Jonathan Lange
createBranch now raises PermissionDenied when users try to create branches |
218 |
"Cannot create branch at '%s'" % branch_path) |
7362.9.11
by Jonathan Lange
create branches from the XMLRPC server using the namespace classes. |
219 |
except InvalidNamespace: |
7459.6.5
by Michael Hudson
kill PERMISSION_DENIED_FAULT_CODE, replace with faults.PermissionDenied |
220 |
return faults.PermissionDenied( |
7362.9.11
by Jonathan Lange
create branches from the XMLRPC server using the namespace classes. |
221 |
"Cannot create branch at '%s'" % branch_path) |
222 |
except NoSuchPerson, e: |
|
7459.6.7
by Michael Hudson
kill NOT_FOUND_FAULT_CODE, replace with faults.NotFound |
223 |
return faults.NotFound( |
7362.9.11
by Jonathan Lange
create branches from the XMLRPC server using the namespace classes. |
224 |
"User/team '%s' does not exist." % e.name) |
225 |
except NoSuchProduct, e: |
|
7459.6.7
by Michael Hudson
kill NOT_FOUND_FAULT_CODE, replace with faults.NotFound |
226 |
return faults.NotFound( |
7362.9.11
by Jonathan Lange
create branches from the XMLRPC server using the namespace classes. |
227 |
"Project '%s' does not exist." % e.name) |
13662.5.1
by Aaron Bentley
Pushing to non-existant sourcepackagename creates. |
228 |
except NoSuchSourcePackageName as e: |
13662.5.3
by Aaron Bentley
Handle invalid source package names. |
229 |
try: |
230 |
getUtility(ISourcePackageNameSet).new(e.name) |
|
231 |
except InvalidName: |
|
232 |
return faults.InvalidSourcePackageName(e.name) |
|
13662.5.1
by Aaron Bentley
Pushing to non-existant sourcepackagename creates. |
233 |
return self.createBranch(login_id, branch_path) |
7362.10.12
by Jonathan Lange
Merge lookup into lp-path |
234 |
except NameLookupFailed, e: |
7459.6.7
by Michael Hudson
kill NOT_FOUND_FAULT_CODE, replace with faults.NotFound |
235 |
return faults.NotFound(str(e)) |
7362.9.11
by Jonathan Lange
create branches from the XMLRPC server using the namespace classes. |
236 |
try: |
237 |
branch = namespace.createBranch( |
|
238 |
BranchType.HOSTED, branch_name, requester) |
|
9738.1.4
by Michael Hudson
a small collection of hacks to get creating a non-ascii branch name failing with a good message |
239 |
except LaunchpadValidationError, e: |
240 |
msg = e.args[0] |
|
241 |
if isinstance(msg, unicode): |
|
242 |
msg = msg.encode('utf-8') |
|
243 |
return faults.PermissionDenied(msg) |
|
244 |
except BranchCreationException, e: |
|
7459.6.5
by Michael Hudson
kill PERMISSION_DENIED_FAULT_CODE, replace with faults.PermissionDenied |
245 |
return faults.PermissionDenied(str(e)) |
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
246 |
|
247 |
if link_func: |
|
248 |
try: |
|
249 |
link_func(branch) |
|
250 |
except Unauthorized: |
|
11121.5.38
by Tim Penhey
Make sure that the branch doesn't exist after a link failure. |
251 |
# We don't want to keep the branch we created.
|
252 |
transaction.abort() |
|
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
253 |
return faults.PermissionDenied( |
11121.5.28
by Tim Penhey
Add tests for product series, inmemory tests still failing. |
254 |
"Cannot create linked branch at '%s'." % path) |
11121.5.27
by Tim Penhey
Can create and link a branch based on the alias. |
255 |
|
256 |
return branch.id |
|
6789.9.1
by Michael Hudson
Turns out that Zope won't let you have decorators on XML-RPC methods. Or rather, |
257 |
return run_with_login(login_id, create_branch) |
6789.6.26
by Jonathan Lange
getBranchInformation tests, interface and implementation. |
258 |
|
259 |
def _canWriteToBranch(self, requester, branch): |
|
260 |
"""Can `requester` write to `branch`?"""
|
|
261 |
if requester == LAUNCHPAD_SERVICES: |
|
262 |
return False |
|
263 |
return (branch.branch_type == BranchType.HOSTED |
|
6789.9.6
by Michael Hudson
respond to most of the review |
264 |
and check_permission('launchpad.Edit', branch)) |
6789.6.26
by Jonathan Lange
getBranchInformation tests, interface and implementation. |
265 |
|
6789.9.1
by Michael Hudson
Turns out that Zope won't let you have decorators on XML-RPC methods. Or rather, |
266 |
def requestMirror(self, login_id, branchID): |
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
267 |
"""See `ICodehostingAPI`."""
|
6789.9.1
by Michael Hudson
Turns out that Zope won't let you have decorators on XML-RPC methods. Or rather, |
268 |
def request_mirror(requester): |
7940.2.5
by Jonathan Lange
Move branch lookup methods to IBranchLookup |
269 |
branch = getUtility(IBranchLookup).get(branchID) |
6789.9.1
by Michael Hudson
Turns out that Zope won't let you have decorators on XML-RPC methods. Or rather, |
270 |
# We don't really care who requests a mirror of a branch.
|
271 |
branch.requestMirror() |
|
272 |
return True |
|
273 |
return run_with_login(login_id, request_mirror) |
|
7055.10.1
by Jonathan Lange
Start work on translatePath. |
274 |
|
9590.4.8
by Michael Hudson
don't use removeSecurityProxy to freely |
275 |
def branchChanged(self, login_id, branch_id, stacked_on_location, |
276 |
last_revision_id, control_string, branch_string, |
|
277 |
repository_string): |
|
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
278 |
"""See `ICodehostingAPI`."""
|
9590.1.73
by Michael Hudson
tweaks to get the puller acceptance tests running |
279 |
def branch_changed(requester): |
9590.4.8
by Michael Hudson
don't use removeSecurityProxy to freely |
280 |
branch_set = getUtility(IBranchLookup) |
281 |
branch = branch_set.get(branch_id) |
|
282 |
if branch is None: |
|
283 |
return faults.NoBranchWithID(branch_id) |
|
284 |
||
9590.1.73
by Michael Hudson
tweaks to get the puller acceptance tests running |
285 |
if requester == LAUNCHPAD_SERVICES: |
286 |
branch = removeSecurityProxy(branch) |
|
287 |
||
13084.5.3
by Aaron Bentley
extract get_db_branch_info from CodehostingAPI.branchChanged. |
288 |
info = get_db_branch_info( |
289 |
stacked_on_location, last_revision_id, control_string, |
|
290 |
branch_string, repository_string) |
|
291 |
branch.branchChanged(**info) |
|
9590.4.8
by Michael Hudson
don't use removeSecurityProxy to freely |
292 |
|
293 |
return True |
|
294 |
||
295 |
return run_with_login(login_id, branch_changed) |
|
9590.1.8
by Michael Hudson
typing |
296 |
|
12707.4.6
by Tim Penhey
db version of translatePath seems to work, now for the inmemory copy. |
297 |
def _serializeBranch(self, requester, branch, trailing_path, |
298 |
force_readonly=False): |
|
7055.10.12
by Jonathan Lange
Add a failing test for CONTROL_PATH and slightly refactor translatePath |
299 |
if requester == LAUNCHPAD_SERVICES: |
300 |
branch = removeSecurityProxy(branch) |
|
301 |
try: |
|
302 |
branch_id = branch.id |
|
303 |
except Unauthorized: |
|
7459.6.11
by Michael Hudson
define and use (once) a return_fault decorator |
304 |
raise faults.PermissionDenied() |
7055.10.12
by Jonathan Lange
Add a failing test for CONTROL_PATH and slightly refactor translatePath |
305 |
if branch.branch_type == BranchType.REMOTE: |
306 |
return None |
|
12707.4.6
by Tim Penhey
db version of translatePath seems to work, now for the inmemory copy. |
307 |
if force_readonly: |
308 |
writable = False |
|
309 |
else: |
|
310 |
writable = self._canWriteToBranch(requester, branch) |
|
7055.10.12
by Jonathan Lange
Add a failing test for CONTROL_PATH and slightly refactor translatePath |
311 |
return ( |
312 |
BRANCH_TRANSPORT, |
|
12707.4.6
by Tim Penhey
db version of translatePath seems to work, now for the inmemory copy. |
313 |
{'id': branch_id, 'writable': writable}, |
7055.10.12
by Jonathan Lange
Add a failing test for CONTROL_PATH and slightly refactor translatePath |
314 |
trailing_path) |
315 |
||
7213.4.8
by Jonathan Lange
_getProduct -> _serializeControlDirectory. |
316 |
def _serializeControlDirectory(self, requester, product_path, |
317 |
trailing_path): |
|
7055.10.16
by Jonathan Lange
Make the first control directory test pass in a fairly crappy way. |
318 |
try: |
9738.1.4
by Michael Hudson
a small collection of hacks to get creating a non-ascii branch name failing with a good message |
319 |
namespace = lookup_branch_namespace(product_path) |
8031.4.4
by Jonathan Lange
Use namespaces to get the control directory. |
320 |
except (InvalidNamespace, NotFoundError): |
321 |
return
|
|
322 |
if not ('.bzr' == trailing_path or trailing_path.startswith('.bzr/')): |
|
8031.4.18
by Jonathan Lange
Comment logic |
323 |
# '.bzr' is OK, '.bzr/foo' is OK, '.bzrfoo' is not.
|
8031.4.4
by Jonathan Lange
Use namespaces to get the control directory. |
324 |
return
|
325 |
default_branch = namespace.target.default_stacked_on_branch |
|
7055.10.22
by Jonathan Lange
Handle the case where there is no default stacked-on branch set. |
326 |
if default_branch is None: |
327 |
return
|
|
7055.10.24
by Jonathan Lange
Return a NotFound for product control directories where the stacked-on branch |
328 |
try: |
12707.7.6
by Tim Penhey
Created a function to do the basic alias assembly. |
329 |
path = branch_id_alias(default_branch) |
7055.10.24
by Jonathan Lange
Return a NotFound for product control directories where the stacked-on branch |
330 |
except Unauthorized: |
331 |
return
|
|
332 |
return ( |
|
7213.4.11
by Jonathan Lange
Return escaped URLs for the stacked-on branch. |
333 |
CONTROL_TRANSPORT, |
12707.7.1
by Tim Penhey
Make the offered stacked location to be based on id. |
334 |
{'default_stack_on': escape(path)}, |
8031.4.4
by Jonathan Lange
Use namespaces to get the control directory. |
335 |
trailing_path) |
7055.10.16
by Jonathan Lange
Make the first control directory test pass in a fairly crappy way. |
336 |
|
12707.4.6
by Tim Penhey
db version of translatePath seems to work, now for the inmemory copy. |
337 |
def _translateBranchIdAlias(self, requester, path): |
338 |
# If the path isn't a branch id alias, nothing more to do.
|
|
339 |
stripped_path = unescape(path.strip('/')) |
|
340 |
if not stripped_path.startswith(BRANCH_ID_ALIAS_PREFIX + '/'): |
|
341 |
return None |
|
342 |
try: |
|
343 |
parts = stripped_path.split('/', 2) |
|
344 |
branch_id = int(parts[1]) |
|
345 |
except (ValueError, IndexError): |
|
346 |
raise faults.PathTranslationError(path) |
|
347 |
branch = getUtility(IBranchLookup).get(branch_id) |
|
348 |
if branch is None: |
|
349 |
raise faults.PathTranslationError(path) |
|
350 |
try: |
|
351 |
trailing = parts[2] |
|
352 |
except IndexError: |
|
353 |
trailing = '' |
|
354 |
return self._serializeBranch(requester, branch, trailing, True) |
|
355 |
||
7055.10.1
by Jonathan Lange
Start work on translatePath. |
356 |
def translatePath(self, requester_id, path): |
9590.1.59
by Michael Hudson
rename ICodehosting -> ICodehostingAPI, Codehosting -> CodehostingAPI |
357 |
"""See `ICodehostingAPI`."""
|
7459.6.11
by Michael Hudson
define and use (once) a return_fault decorator |
358 |
@return_fault
|
7055.10.9
by Jonathan Lange
Start handling private branches. |
359 |
def translate_path(requester): |
360 |
if not path.startswith('/'): |
|
361 |
return faults.InvalidPath(path) |
|
12707.4.6
by Tim Penhey
db version of translatePath seems to work, now for the inmemory copy. |
362 |
branch = self._translateBranchIdAlias(requester, path) |
363 |
if branch is not None: |
|
364 |
return branch |
|
7055.10.9
by Jonathan Lange
Start handling private branches. |
365 |
stripped_path = path.strip('/') |
366 |
for first, second in iter_split(stripped_path, '/'): |
|
9738.1.4
by Michael Hudson
a small collection of hacks to get creating a non-ascii branch name failing with a good message |
367 |
first = unescape(first) |
7055.10.16
by Jonathan Lange
Make the first control directory test pass in a fairly crappy way. |
368 |
# Is it a branch?
|
11121.5.2
by Jonathan Lange
Use a constant, not a literal. |
369 |
if first.startswith(BRANCH_ALIAS_PREFIX + '/'): |
11121.5.5
by Jonathan Lange
Test for actual paths sent by the client, rather than our best guesses. |
370 |
try: |
11121.5.31
by Tim Penhey
More tests and tweaks to make the tests pass. |
371 |
# translatePath('/+branch/.bzr') *must* return not
|
372 |
# found, otherwise bzr will look for it and we don't
|
|
373 |
# have a global bzr dir.
|
|
374 |
lp_path = first[len(BRANCH_ALIAS_PREFIX + '/'):] |
|
11121.5.38
by Tim Penhey
Make sure that the branch doesn't exist after a link failure. |
375 |
if lp_path == '.bzr' or lp_path.startswith('.bzr/'): |
11121.5.31
by Tim Penhey
More tests and tweaks to make the tests pass. |
376 |
raise faults.PathTranslationError(path) |
377 |
branch, trailing = getUtility( |
|
378 |
IBranchLookup).getByLPPath(lp_path) |
|
11474.5.16
by Tim Penhey
Test invalid product name. |
379 |
except (InvalidProductName, NoLinkedBranch, |
380 |
CannotHaveLinkedBranch): |
|
381 |
# If we get one of these errors, then there is no
|
|
382 |
# point walking back through the path parts.
|
|
383 |
break
|
|
384 |
except (NameLookupFailed, InvalidNamespace): |
|
11121.5.33
by Tim Penhey
Comment tweaks. |
385 |
# The reason we're doing it is that getByLPPath thinks
|
386 |
# that 'foo/.bzr' is a request for the '.bzr' series
|
|
387 |
# of a product.
|
|
11121.5.5
by Jonathan Lange
Test for actual paths sent by the client, rather than our best guesses. |
388 |
continue
|
11121.5.31
by Tim Penhey
More tests and tweaks to make the tests pass. |
389 |
if trailing is None: |
390 |
trailing = '' |
|
11121.5.5
by Jonathan Lange
Test for actual paths sent by the client, rather than our best guesses. |
391 |
second = '/'.join([trailing, second]).strip('/') |
11121.5.1
by Jonathan Lange
Begin supporting +branch over codehosting. |
392 |
else: |
393 |
branch = getUtility(IBranchLookup).getByUniqueName(first) |
|
7055.10.16
by Jonathan Lange
Make the first control directory test pass in a fairly crappy way. |
394 |
if branch is not None: |
395 |
branch = self._serializeBranch(requester, branch, second) |
|
7459.6.11
by Michael Hudson
define and use (once) a return_fault decorator |
396 |
if branch is None: |
7055.10.16
by Jonathan Lange
Make the first control directory test pass in a fairly crappy way. |
397 |
break
|
398 |
return branch |
|
7055.10.22
by Jonathan Lange
Handle the case where there is no default stacked-on branch set. |
399 |
# Is it a product control directory?
|
7213.4.8
by Jonathan Lange
_getProduct -> _serializeControlDirectory. |
400 |
product = self._serializeControlDirectory( |
401 |
requester, first, second) |
|
7055.10.16
by Jonathan Lange
Make the first control directory test pass in a fairly crappy way. |
402 |
if product is not None: |
403 |
return product |
|
7459.6.11
by Michael Hudson
define and use (once) a return_fault decorator |
404 |
raise faults.PathTranslationError(path) |
7055.10.9
by Jonathan Lange
Start handling private branches. |
405 |
return run_with_login(requester_id, translate_path) |