13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
1 |
Librarian Access |
2 |
================ |
|
3 |
||
4 |
The librarian is a file storage service for launchpad. Conceptually |
|
5 |
similar to other file storage API's like S3, it is used to store binary |
|
6 |
or large content - bug attachments, package builds, images and so on. |
|
7 |
||
8 |
Content in the librarian can be exposed at different urls. To expose |
|
9 |
some content use a LibraryFileAlias. Private content is supported as |
|
10 |
well - for that tokens are added to permit access for a limited time by |
|
11 |
a client - each time a client attempts to dereference a private |
|
12 |
LibraryFileAlias a token is emitted. |
|
13 |
||
14 |
||
15 |
Deployment notes |
|
16 |
================ |
|
17 |
||
18 |
(These may seem a bit out of place - they are, but they need to be |
|
19 |
written down somewhere, and the deployment choices inform the |
|
20 |
implementation choices). |
|
21 |
||
22 |
The basics are simple: The librarian talks to clients. However |
|
23 |
restricted file access makes things a little more complex. As the |
|
24 |
librarian itself doesn't do SSL processing, and we want restricted files |
|
25 |
to be kept confidential the librarian will need a hint from the SSL |
|
26 |
front end that SSL was in fact used. The semi standard header Front-End- |
|
27 |
Https can be used for this if we filter it in incoming requests from |
|
28 |
clients. |
|
29 |
||
30 |
||
31 |
setUp |
|
32 |
----- |
|
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
33 |
|
12393.27.5
by Danilo Segan
Merge with latest accordionoverlay. |
34 |
>>> from canonical.database.sqlbase import session_store |
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
35 |
>>> from lp.services.librarian.model import TimeLimitedToken |
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
36 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
37 |
|
38 |
High Level |
|
39 |
---------- |
|
4989.3.2
by Francis J. Lacoste
Convert moin header. |
40 |
|
11716.1.12
by Curtis Hovey
Sorted imports in doctests. |
41 |
>>> from StringIO import StringIO |
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
42 |
>>> from lp.services.librarian.interfaces import ( |
12393.27.5
by Danilo Segan
Merge with latest accordionoverlay. |
43 |
... ILibraryFileAliasSet) |
4195.1.12
by Brad Crittenden
Post-review changes |
44 |
>>> data = 'This is some data' |
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
45 |
|
46 |
We can create LibraryFileAliases using the ILibraryFileAliasSet utility. |
|
4195.1.1
by Brad Crittenden
Implement upload and management of files associated with a product release. |
47 |
This name is a mouthful, but is consistent with the rest of our naming. |
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
48 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
49 |
>>> lfas = getUtility(ILibraryFileAliasSet) |
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
50 |
>>> from lp.services.librarian.interfaces import NEVER_EXPIRES |
4195.1.12
by Brad Crittenden
Post-review changes |
51 |
>>> alias = lfas.create( |
52 |
... 'text.txt', len(data), StringIO(data), 'text/plain', NEVER_EXPIRES |
|
53 |
... ) |
|
54 |
>>> alias.mimetype |
|
55 |
u'text/plain' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
56 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
57 |
>>> alias.filename |
58 |
u'text.txt' |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
59 |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
60 |
We may wish to set an expiry timestamp on the file. The NEVER_EXPIRES |
61 |
constant means the file will never be removed from the Librarian, and |
|
62 |
because of this should probably never be used. |
|
63 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
64 |
>>> alias.expires == NEVER_EXPIRES |
65 |
True |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
66 |
|
5911.2.19
by Francis J. Lacoste
Style based on salgado's review. |
67 |
>>> alias = lfas.create( |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
68 |
... 'text.txt', len(data), StringIO(data), 'text/plain') |
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
69 |
|
70 |
The default expiry of None means the file will expire a few days after |
|
71 |
it is no longer referenced in the database. |
|
72 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
73 |
>>> alias.expires is None |
74 |
True |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
75 |
|
4989.3.3
by Francis J. Lacoste
Expose date_created column in interface. |
76 |
The creation timestamp of the LibraryFileAlias is available in the |
77 |
date_created attribute. |
|
78 |
||
79 |
>>> alias.date_created |
|
80 |
datetime.datetime(...) |
|
81 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
82 |
We can retrieve the LibraryFileAlias we just created using its ID or |
83 |
sha1. |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
84 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
85 |
>>> org_alias_id = alias.id |
86 |
>>> alias = lfas[org_alias_id] |
|
87 |
>>> alias.id == org_alias_id |
|
88 |
True |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
89 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
90 |
>>> org_alias_id in [a.id for a in lfas.findBySHA1(alias.content.sha1)] |
91 |
True |
|
3691.164.16
by Guilherme Salgado
Lots of fixes and tests suggested by Bjorn |
92 |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
93 |
We can get its URL too |
94 |
||
14605.1.1
by Curtis Hovey
Moved canonical.config to lp.services. |
95 |
>>> from lp.services.config import config |
4195.1.12
by Brad Crittenden
Post-review changes |
96 |
>>> import re |
97 |
>>> re.search( |
|
13980.2.3
by Jeroen Vermeulen
Some undetected lint, and some caused by auto-formatting. |
98 |
... r'^%s\d+/text.txt$' % config.librarian.download_url, |
99 |
... alias.http_url |
|
100 |
... ) is not None |
|
4195.1.12
by Brad Crittenden
Post-review changes |
101 |
True |
3691.9.26
by Guilherme Salgado
Fix https://launchpad.net/launchpad/+bug/76743 Refactor of LibraryFileAlias.[secure_]url |
102 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
103 |
Librarian also serves the same file through https, we use this for |
104 |
branding and similar inline-presented objects which would trigger |
|
105 |
security warnings on https pages otherwise. |
|
3691.9.26
by Guilherme Salgado
Fix https://launchpad.net/launchpad/+bug/76743 Refactor of LibraryFileAlias.[secure_]url |
106 |
|
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
107 |
>>> re.search(r'^https://.+/\d+/text.txt$', alias.https_url) is not None |
4195.1.12
by Brad Crittenden
Post-review changes |
108 |
True |
3691.9.26
by Guilherme Salgado
Fix https://launchpad.net/launchpad/+bug/76743 Refactor of LibraryFileAlias.[secure_]url |
109 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
110 |
And we even have a convenient method which returns either the http URL |
111 |
or the https one, depending on a config value. |
|
3691.9.26
by Guilherme Salgado
Fix https://launchpad.net/launchpad/+bug/76743 Refactor of LibraryFileAlias.[secure_]url |
112 |
|
5773.4.6
by Curtis Hovey
Added support for vhost to lazr and launchpad. |
113 |
>>> config.vhosts.use_https |
4195.1.12
by Brad Crittenden
Post-review changes |
114 |
False |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
115 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
116 |
>>> re.search( |
13980.2.3
by Jeroen Vermeulen
Some undetected lint, and some caused by auto-formatting. |
117 |
... r'^%s\d+/text.txt$' % config.librarian.download_url, |
118 |
... alias.getURL() |
|
119 |
... ) is not None |
|
4195.1.12
by Brad Crittenden
Post-review changes |
120 |
True |
3691.9.26
by Guilherme Salgado
Fix https://launchpad.net/launchpad/+bug/76743 Refactor of LibraryFileAlias.[secure_]url |
121 |
|
5773.4.10
by Curtis Hovey
Revisions per review. |
122 |
>>> from textwrap import dedent |
123 |
>>> test_data = dedent(""" |
|
13980.2.3
by Jeroen Vermeulen
Some undetected lint, and some caused by auto-formatting. |
124 |
... [librarian] |
125 |
... use_https: true |
|
126 |
... """) |
|
5773.4.10
by Curtis Hovey
Revisions per review. |
127 |
>>> config.push('test', test_data) |
4195.1.12
by Brad Crittenden
Post-review changes |
128 |
>>> re.search( |
13980.2.3
by Jeroen Vermeulen
Some undetected lint, and some caused by auto-formatting. |
129 |
... r'^https://.+/\d+/text.txt$', alias.https_url |
130 |
... ) is not None |
|
4195.1.12
by Brad Crittenden
Post-review changes |
131 |
True |
3691.9.26
by Guilherme Salgado
Fix https://launchpad.net/launchpad/+bug/76743 Refactor of LibraryFileAlias.[secure_]url |
132 |
|
5723.9.13
by Brad Crittenden
Corrected HTTP_X_SCHEME in the doc test. |
133 |
However, we can force the use of HTTP by setting the 'HTTP_X_SCHEME' |
5723.9.1
by Brad Crittenden
Redirect LibraryFileAliases based on the X-SCHEME request variable to allow serving them over HTTP even when the site is configured for HTTPS |
134 |
header in the request to 'http', even when 'use_https' is True. |
135 |
||
11716.1.12
by Curtis Hovey
Sorted imports in doctests. |
136 |
>>> from zope.component import getMultiAdapter |
14600.2.2
by Curtis Hovey
Moved webapp to lp.services. |
137 |
>>> from lp.services.webapp.servers import LaunchpadTestRequest |
5723.9.1
by Brad Crittenden
Redirect LibraryFileAliases based on the X-SCHEME request variable to allow serving them over HTTP even when the site is configured for HTTPS |
138 |
>>> from urlparse import urlparse |
139 |
>>> request = LaunchpadTestRequest( |
|
13980.2.3
by Jeroen Vermeulen
Some undetected lint, and some caused by auto-formatting. |
140 |
... environ={'REQUEST_METHOD': 'GET', 'HTTP_X_SCHEME': 'http'}) |
5723.9.1
by Brad Crittenden
Redirect LibraryFileAliases based on the X-SCHEME request variable to allow serving them over HTTP even when the site is configured for HTTPS |
141 |
>>> view = getMultiAdapter((alias,request), name='+index') |
142 |
>>> view.initialize() |
|
143 |
>>> print urlparse(request.response.getHeader('Location'))[0] |
|
144 |
http |
|
145 |
||
146 |
When the incoming scheme is 'https' then the redirect scheme is |
|
147 |
unaffected. |
|
148 |
||
149 |
>>> request = LaunchpadTestRequest( |
|
13980.2.3
by Jeroen Vermeulen
Some undetected lint, and some caused by auto-formatting. |
150 |
... environ={'REQUEST_METHOD': 'GET', 'HTTP_X_SCHEME': 'https'}) |
5723.9.1
by Brad Crittenden
Redirect LibraryFileAliases based on the X-SCHEME request variable to allow serving them over HTTP even when the site is configured for HTTPS |
151 |
>>> view = getMultiAdapter((alias,request), name='+index') |
152 |
>>> view.initialize() |
|
153 |
>>> print urlparse(request.response.getHeader('Location'))[0] |
|
154 |
https |
|
3564.6.38
by Diogo Matsubara
Fixes bug 30370 (Graphics from Librarian over HTTP cause browser warnings on Launchpad over HTTPS) |
155 |
|
5723.9.8
by Brad Crittenden
Reset use_https to the original value as expected by subsequent tests. |
156 |
Reset 'use_https' to its original state. |
157 |
||
5773.4.6
by Curtis Hovey
Added support for vhost to lazr and launchpad. |
158 |
>>> test_config_data = config.pop('test') |
5723.9.8
by Brad Crittenden
Reset use_https to the original value as expected by subsequent tests. |
159 |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
160 |
However, we can't access its contents until we have committed |
161 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
162 |
>>> alias.open() |
163 |
Traceback (most recent call last): |
|
164 |
[...] |
|
165 |
LookupError: ... |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
166 |
|
167 |
Once we commit the transaction, LibraryFileAliases can be accessed like |
|
168 |
files. |
|
169 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
170 |
>>> import transaction |
171 |
>>> transaction.commit() |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
172 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
173 |
>>> alias.open() |
174 |
>>> alias.read() |
|
175 |
'This is some data' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
176 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
177 |
>>> alias.close() |
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
178 |
|
179 |
We can also read it in chunks. |
|
180 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
181 |
>>> alias.open() |
182 |
>>> alias.read(2) |
|
183 |
'Th' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
184 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
185 |
>>> alias.read(6) |
186 |
'is is ' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
187 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
188 |
>>> alias.read() |
189 |
'some data' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
190 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
191 |
>>> alias.close() |
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
192 |
|
193 |
If you don't want to read the file in chunks you can neglect to call |
|
194 |
open() and close(). |
|
195 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
196 |
>>> alias.read() |
197 |
'This is some data' |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
198 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
199 |
Each alias also has an expiry date associated with it, the default of |
200 |
None meaning the file will expire a few days after nothing references it |
|
201 |
any more: |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
202 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
203 |
>>> alias.expires is None |
204 |
True |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
205 |
|
6422.1.1
by Muharem Hrnjadovic
bug fixed |
206 |
Closing an alias repeatedly and/or without opening it beforehand is |
207 |
tolerated and will not result in exceptions being raised. |
|
208 |
||
209 |
>>> alias.close() |
|
210 |
>>> alias.close() |
|
211 |
||
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
212 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
213 |
Low Level |
214 |
--------- |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
215 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
216 |
We can also use the ILibrarianClient Utility directly to store and |
217 |
access files in the Librarian. |
|
1654
by Canonical.com Patch Queue Manager
MailStorage [r=stevea] |
218 |
|
14606.2.3
by William Grant
canonical.librarian.interfaces -> lp.services.librarian.interfaces.client |
219 |
>>> from lp.services.librarian.interfaces.client import ILibrarianClient |
4195.1.12
by Brad Crittenden
Post-review changes |
220 |
>>> client = getUtility(ILibrarianClient) |
221 |
>>> aid = client.addFile( |
|
222 |
... 'text.txt', len(data), StringIO(data), 'text/plain', NEVER_EXPIRES |
|
223 |
... ) |
|
224 |
>>> transaction.commit() |
|
225 |
>>> f = client.getFileByAlias(aid) |
|
226 |
>>> f.read() |
|
227 |
'This is some data' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
228 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
229 |
>>> url = client.getURLForAlias(aid) |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
230 |
>>> re.search( |
231 |
... r'^%s\d+/text.txt$' % config.librarian.download_url, url |
|
232 |
... ) is not None |
|
4195.1.12
by Brad Crittenden
Post-review changes |
233 |
True |
2398
by Canonical.com Patch Queue Manager
Librarian buildd_download_url support. r=spiv |
234 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
235 |
When secure=True, the returned url has the id as part of the domain name |
236 |
and the protocol is https: |
|
7675.809.18
by Robert Collins
Extend the private librarian concept to have unique domains to get unique security contexts on browsers. |
237 |
|
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
238 |
>>> expected = r'^https://i%d\..+:\d+/%d/text.txt$' % (aid, aid) |
239 |
>>> found = client.getURLForAlias(aid, secure=True) |
|
240 |
>>> re.search(expected, found) is not None |
|
7675.809.18
by Robert Collins
Extend the private librarian concept to have unique domains to get unique security contexts on browsers. |
241 |
True |
242 |
||
11502.1.1
by Robert Collins
Instrument librarian access via timelines. |
243 |
Librarian reads are logged in the request timeline. |
244 |
||
13931.2.1
by Steve Kowalik
Chip away at canonical.lazr a little more. |
245 |
>>> from lazr.restful.utils import get_current_browser_request |
11502.1.1
by Robert Collins
Instrument librarian access via timelines. |
246 |
>>> from lp.services.timeline.requesttimeline import get_request_timeline |
247 |
>>> request = get_current_browser_request() |
|
248 |
>>> timeline = get_request_timeline(request) |
|
249 |
>>> f = client.getFileByAlias(aid) |
|
250 |
>>> action = timeline.actions[-1] |
|
251 |
>>> action.category |
|
252 |
'librarian-connection' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
253 |
|
11502.1.1
by Robert Collins
Instrument librarian access via timelines. |
254 |
>>> action.detail.endswith('/text.txt') |
255 |
True |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
256 |
|
11502.1.1
by Robert Collins
Instrument librarian access via timelines. |
257 |
>>> _unused = f.read() |
258 |
>>> action = timeline.actions[-1] |
|
259 |
>>> action.category |
|
260 |
'librarian-read' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
261 |
|
11502.1.1
by Robert Collins
Instrument librarian access via timelines. |
262 |
>>> action.detail.endswith('/text.txt') |
263 |
True |
|
264 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
265 |
At this level we can also reverse the transactional semantics by using |
266 |
the remoteAddFile instead of the addFile method. In this case, the |
|
267 |
database rows are added by the Librarian, which means that the file is |
|
268 |
downloadable immediately and will exist even if the client transaction |
|
269 |
rolls back. However, the records in the database will not be visible to |
|
270 |
the client until it begins a new transaction. |
|
2449
by Canonical.com Patch Queue Manager
[r=spiv] script librarian logging |
271 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
272 |
>>> url = client.remoteAddFile( |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
273 |
... 'text.txt', len(data), StringIO(data), 'text/plain') |
4195.1.12
by Brad Crittenden
Post-review changes |
274 |
>>> print url |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
275 |
http://.../text.txt |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
276 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
277 |
>>> from urllib2 import urlopen |
278 |
>>> urlopen(url).read() |
|
279 |
'This is some data' |
|
2449
by Canonical.com Patch Queue Manager
[r=spiv] script librarian logging |
280 |
|
281 |
If we abort the transaction, it is still in there |
|
282 |
||
4195.1.12
by Brad Crittenden
Post-review changes |
283 |
>>> transaction.abort() |
284 |
>>> urlopen(url).read() |
|
285 |
'This is some data' |
|
2449
by Canonical.com Patch Queue Manager
[r=spiv] script librarian logging |
286 |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
287 |
You can also set the expiry date on the file this way too: |
288 |
||
8499.2.4
by Guilherme Salgado
A couple improvements as suggested by Martin |
289 |
>>> from datetime import date, datetime |
4195.1.12
by Brad Crittenden
Post-review changes |
290 |
>>> from pytz import utc |
291 |
>>> url = client.remoteAddFile( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
292 |
... 'text.txt', len(data), StringIO(data), 'text/plain', |
293 |
... expires=datetime(2005,9,1,12,0,0, tzinfo=utc)) |
|
4195.1.12
by Brad Crittenden
Post-review changes |
294 |
>>> transaction.abort() |
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
295 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
296 |
To check the expiry is set, we need to extract the alias id from the |
297 |
URL. remoteAddFile deliberatly returns the URL instead of the alias id |
|
298 |
because, except for test cases, the URL is the only thing useful |
|
299 |
(because the client can't see the database records yet). |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
300 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
301 |
>>> import re |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
302 |
>>> match = re.search('/(\d+)/', url) |
4195.1.12
by Brad Crittenden
Post-review changes |
303 |
>>> alias_id = int(match.group(1)) |
304 |
>>> alias = lfas[alias_id] |
|
305 |
>>> print alias.expires.isoformat() |
|
306 |
2005-09-01T12:00:00+00:00 |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
307 |
|
4989.3.2
by Francis J. Lacoste
Convert moin header. |
308 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
309 |
Restricted Librarian |
310 |
-------------------- |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
311 |
|
312 |
Some files should not be generally available publicly. If you know the |
|
313 |
URL, any file can be retrieved directly from the librarian. For this |
|
5911.2.19
by Francis J. Lacoste
Style based on salgado's review. |
314 |
reason, there is a restricted librarian to which access is restricted |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
315 |
(at the system-level). This means that only Launchpad has direct access |
316 |
to the host. You use the IRestrictedLibrarianClient to access this |
|
317 |
librarian. |
|
318 |
||
319 |
>>> from zope.interface.verify import verifyObject |
|
14606.2.3
by William Grant
canonical.librarian.interfaces -> lp.services.librarian.interfaces.client |
320 |
>>> from lp.services.librarian.interfaces.client import IRestrictedLibrarianClient |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
321 |
>>> restricted_client = getUtility(IRestrictedLibrarianClient) |
322 |
>>> verifyObject(IRestrictedLibrarianClient, restricted_client) |
|
323 |
True |
|
324 |
||
325 |
File alias uploaded through the restricted librarian have the restricted |
|
326 |
attribute set. |
|
327 |
||
328 |
>>> private_content = 'This is private data.' |
|
329 |
>>> private_file_id = restricted_client.addFile( |
|
330 |
... 'private.txt', len(private_content), StringIO(private_content), |
|
331 |
... 'text/plain') |
|
332 |
>>> file_alias = getUtility(ILibraryFileAliasSet)[private_file_id] |
|
333 |
>>> file_alias.restricted |
|
334 |
True |
|
335 |
||
12670.2.13
by William Grant
Fix tests. |
336 |
>>> transaction.commit() |
337 |
>>> file_alias.open() |
|
338 |
>>> print file_alias.read() |
|
339 |
This is private data. |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
340 |
|
12670.2.13
by William Grant
Fix tests. |
341 |
>>> file_alias.close() |
342 |
||
343 |
Restricted files are accessible with HTTP on a private domain. |
|
344 |
||
5911.2.10
by Francis J. Lacoste
Use proper client in LibraryFileAlias. |
345 |
>>> print file_alias.http_url |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
346 |
http://.../private.txt |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
347 |
|
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
348 |
>>> file_alias.http_url.startswith( |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
349 |
... config.librarian.restricted_download_url) |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
350 |
True |
5911.2.10
by Francis J. Lacoste
Use proper client in LibraryFileAlias. |
351 |
|
12670.2.13
by William Grant
Fix tests. |
352 |
They can also be accessed externally using a time-limited token appended |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
353 |
to their private_url. Possession of a token is sufficient to grant |
354 |
access to a file, regardless of who is logged in. getURL can be asked to |
|
355 |
provide such a token. |
|
12670.2.13
by William Grant
Fix tests. |
356 |
|
357 |
>>> token_url = file_alias.getURL(include_token=True) |
|
358 |
>>> print token_url |
|
359 |
https://i...restricted.../private.txt?token=... |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
360 |
|
12670.2.13
by William Grant
Fix tests. |
361 |
>>> token_url.startswith('https://i%d.restricted.' % file_alias.id) |
362 |
True |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
363 |
|
12670.2.13
by William Grant
Fix tests. |
364 |
>>> private_path = TimeLimitedToken.url_to_token_path( |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
365 |
... file_alias.private_url) |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
366 |
>>> token_url.endswith(session_store().find( |
367 |
... TimeLimitedToken, path=private_path).any().token) |
|
12670.2.13
by William Grant
Fix tests. |
368 |
True |
369 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
370 |
LibraryFileAliasView doesn't work on restricted files. This is a |
371 |
temporary measure until we're sure no restricted files leak into the |
|
372 |
traversal hierarchy. |
|
12670.2.13
by William Grant
Fix tests. |
373 |
|
374 |
>>> view = getMultiAdapter((file_alias, request), name='+index') |
|
375 |
>>> view.initialize() |
|
376 |
Traceback (most recent call last): |
|
377 |
... |
|
378 |
AssertionError |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
379 |
|
5911.2.19
by Francis J. Lacoste
Style based on salgado's review. |
380 |
If you try to retrieve this file through the standard ILibrarianClient, |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
381 |
you'll get a DownloadFailed error. |
382 |
||
383 |
>>> client.getFileByAlias(private_file_id) |
|
384 |
Traceback (most recent call last): |
|
385 |
... |
|
5911.2.6
by Francis J. Lacoste
Prevent retrieving restricted file alias through wrong client. |
386 |
DownloadFailed: Alias ... cannot be downloaded from this client. |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
387 |
|
388 |
>>> client.getURLForAlias(private_file_id) |
|
389 |
Traceback (most recent call last): |
|
390 |
... |
|
5911.2.6
by Francis J. Lacoste
Prevent retrieving restricted file alias through wrong client. |
391 |
DownloadFailed: Alias ... cannot be downloaded from this client. |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
392 |
|
393 |
But using the restricted librarian will work: |
|
394 |
||
395 |
>>> restricted_client.getFileByAlias(private_file_id) |
|
14606.2.2
by William Grant
Move canonical.librarian.{client,utils} to lp.services.librarian. |
396 |
<lp.services.librarian.client._File...> |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
397 |
|
398 |
>>> file_url = restricted_client.getURLForAlias(private_file_id) |
|
399 |
>>> print file_url |
|
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
400 |
http://.../private.txt |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
401 |
|
402 |
Trying to access that file directly on the normal librarian will fail |
|
403 |
(by switching the port) |
|
404 |
||
405 |
>>> sneaky_url = file_url.replace( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
406 |
... config.librarian.restricted_download_url, |
407 |
... config.librarian.download_url) |
|
5911.2.7
by Francis J. Lacoste
Add configuration for restricted librarian. |
408 |
>>> urlopen(sneaky_url).read() |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
409 |
Traceback (most recent call last): |
410 |
... |
|
8697.27.21
by Gary Poster
switch back to HTTPError: this is using urlopen, not testbrowser. |
411 |
HTTPError: HTTP Error 404: Not Found |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
412 |
|
413 |
But downloading it from the restricted host, will work. |
|
414 |
||
415 |
>>> print urlopen(file_url).read() |
|
416 |
This is private data. |
|
417 |
||
418 |
Trying to retrieve a non-restricted file from the restricted librarian |
|
419 |
also fails: |
|
420 |
||
421 |
>>> public_content = 'This is public data.' |
|
422 |
>>> public_file_id = getUtility(ILibrarianClient).addFile( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
423 |
... 'public.txt', len(public_content), StringIO(public_content), |
424 |
... 'text/plain') |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
425 |
>>> file_alias = getUtility(ILibraryFileAliasSet)[public_file_id] |
426 |
>>> file_alias.restricted |
|
5911.2.6
by Francis J. Lacoste
Prevent retrieving restricted file alias through wrong client. |
427 |
False |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
428 |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
429 |
>>> transaction.commit() |
430 |
||
431 |
>>> restricted_client.getURLForAlias(public_file_id) |
|
432 |
Traceback (most recent call last): |
|
433 |
... |
|
434 |
DownloadFailed: ... |
|
435 |
||
436 |
>>> restricted_client.getFileByAlias(public_file_id) |
|
437 |
Traceback (most recent call last): |
|
438 |
... |
|
439 |
DownloadFailed: ... |
|
440 |
||
5911.2.19
by Francis J. Lacoste
Style based on salgado's review. |
441 |
The remoteAddFile() on the restricted client, also creates a restricted |
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
442 |
file: |
443 |
||
444 |
>>> url = restricted_client.remoteAddFile( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
445 |
... 'another-private.txt', len(private_content), |
446 |
... StringIO(private_content), 'text/plain') |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
447 |
>>> print url |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
448 |
http://.../another-private.txt |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
449 |
|
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
450 |
>>> url.startswith(config.librarian.restricted_download_url) |
451 |
True |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
452 |
|
5911.2.12
by Francis J. Lacoste
Make restrited remoteAddFile() work. |
453 |
The file can then immediately be retrieved: |
454 |
||
455 |
>>> print urlopen(url).read() |
|
456 |
This is private data. |
|
457 |
||
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
458 |
Another way to create a restricted file is by using the restricted |
459 |
parameter to ILibraryFileAliasSet: |
|
460 |
||
461 |
>>> restricted_file = getUtility(ILibraryFileAliasSet).create( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
462 |
... 'yet-another-private.txt', len(private_content), |
463 |
... StringIO(private_content), 'text/plain', restricted=True) |
|
5911.2.3
by Francis J. Lacoste
Add initial tests for the restricted librarian. |
464 |
>>> restricted_file.restricted |
465 |
True |
|
466 |
||
5911.2.13
by Francis J. Lacoste
Make sure that searching for a SHA doesn't leak the restricted files. As a bonus, actually make getAliases() work. |
467 |
Even if one has the SHA1 of the file, searching the librarian for it |
468 |
will only return the file if it was in the same restriction space. |
|
469 |
||
470 |
So searching for the private content on the public librarian will fail: |
|
471 |
||
472 |
>>> transaction.commit() |
|
473 |
>>> search_query = "search?digest=%s" % restricted_file.content.sha1 |
|
474 |
>>> print urlopen(config.librarian.download_url + search_query).read() |
|
475 |
0 |
|
476 |
||
477 |
But on the restricted server, this will work: |
|
478 |
||
479 |
>>> result = urlopen( |
|
480 |
... config.librarian.restricted_download_url + search_query).read() |
|
481 |
>>> result = result.splitlines() |
|
482 |
>>> print result[0] |
|
483 |
3 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
484 |
|
6642.1.9
by Celso Providelo
fixing test failures. |
485 |
>>> sorted(file_path.split('/')[1] for file_path in result[1:]) |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
486 |
['another-private.txt', 'private.txt', 'yet-another-private.txt'] |
487 |
||
488 |
||
489 |
Odds and Sods |
|
490 |
------------- |
|
491 |
||
492 |
An UploadFailed will be raised if you try to create a file with no |
|
493 |
content |
|
1743
by Canonical.com Patch Queue Manager
[trivial] Librarian upload shouldn't hang if passed an empty file |
494 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
495 |
>>> client.addFile('test.txt', 0, StringIO('hello'), 'text/plain') |
496 |
Traceback (most recent call last): |
|
497 |
[...] |
|
498 |
UploadFailed: Invalid length: 0 |
|
1817
by Canonical.com Patch Queue Manager
Add an assertion to catch mismatches between stated file size and bytes read, avoiding a hang. r=SteveA |
499 |
|
12225.2.1
by Robert Collins
Allow zero length files in the librarian - the server supports them and has since a rev in the early 2000 range. |
500 |
If you really want a zero length file you can do it: |
501 |
||
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
502 |
>>> aid = client.addFile( |
503 |
... 'test.txt', 0, StringIO(''), 'text/plain', allow_zero_length=True) |
|
12225.2.1
by Robert Collins
Allow zero length files in the librarian - the server supports them and has since a rev in the early 2000 range. |
504 |
>>> transaction.commit() |
505 |
>>> f = client.getFileByAlias(aid) |
|
506 |
>>> f.read() |
|
507 |
'' |
|
508 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
509 |
An AssertionError will be raised if the number of bytes that could be |
510 |
read from the file don't match the declared size. |
|
1743
by Canonical.com Patch Queue Manager
[trivial] Librarian upload shouldn't hang if passed an empty file |
511 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
512 |
>>> client.addFile('test.txt', 42, StringIO(''), 'text/plain') |
513 |
Traceback (most recent call last): |
|
514 |
[...] |
|
515 |
AssertionError: size is 42, but 0 were read from the file |
|
1743
by Canonical.com Patch Queue Manager
[trivial] Librarian upload shouldn't hang if passed an empty file |
516 |
|
2326
by Canonical.com Patch Queue Manager
[r=BjornT] Fix for bug 1785: Librarian doesn't handle unicode filenames properly. Plus more tests. |
517 |
Filenames with spaces in them work. |
518 |
||
12393.27.5
by Danilo Segan
Merge with latest accordionoverlay. |
519 |
>>> aid = client.addFile( |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
520 |
... 'hot dog', len(data), StringIO(data), 'text/plain') |
4195.1.12
by Brad Crittenden
Post-review changes |
521 |
>>> transaction.commit() |
522 |
>>> f = client.getFileByAlias(aid) |
|
523 |
>>> f.read() |
|
524 |
'This is some data' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
525 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
526 |
>>> url = client.getURLForAlias(aid) |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
527 |
>>> re.search(r'/\d+/hot%20dog$', url) is not None |
4195.1.12
by Brad Crittenden
Post-review changes |
528 |
True |
2326
by Canonical.com Patch Queue Manager
[r=BjornT] Fix for bug 1785: Librarian doesn't handle unicode filenames properly. Plus more tests. |
529 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
530 |
Unicode file names work. Note that the filename in the resulting URL is |
531 |
encoded as UTF-8. |
|
2326
by Canonical.com Patch Queue Manager
[r=BjornT] Fix for bug 1785: Librarian doesn't handle unicode filenames properly. Plus more tests. |
532 |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
533 |
>>> aid = client.addFile( |
534 |
... u'Yow\N{INTERROBANG}', len(data), StringIO(data), 'text/plain') |
|
4195.1.12
by Brad Crittenden
Post-review changes |
535 |
>>> transaction.commit() |
536 |
>>> f = client.getFileByAlias(aid) |
|
537 |
>>> f.read() |
|
538 |
'This is some data' |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
539 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
540 |
>>> url = client.getURLForAlias(aid) |
3691.57.70
by Stuart Bishop
Fix some librarian.txt tests |
541 |
>>> re.search(r'/\d+/Yow%E2%80%BD$', url) is not None |
4195.1.12
by Brad Crittenden
Post-review changes |
542 |
True |
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
543 |
|
544 |
Files will get garbage collected on production systems as per |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
545 |
LibrarianGarbageCollection. If you request the URL of a deleted file, |
546 |
you will be given None |
|
2764
by Canonical.com Patch Queue Manager
[r=spiv] LibrarianGarbageCollection client API |
547 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
548 |
>>> alias = lfas[36] |
7675.415.4
by Abel Deuring
adjust storm classes to no longer define and use the dropped columns LibraryFileContent.deleted, LibraryFileContent.datemirrored (exception: librarian_gc.py is not yet fixed); add a property 'deleted' to LibraryFileAlias; replace usage of LibraryFileAlias.content.deleted by LibraryFileAlias.deleted; fix failing tests, except test_gc.py |
549 |
>>> alias.deleted |
4195.1.12
by Brad Crittenden
Post-review changes |
550 |
True |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
551 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
552 |
>>> alias.http_url is None |
553 |
True |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
554 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
555 |
>>> alias.https_url is None |
556 |
True |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
557 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
558 |
>>> alias.getURL() is None |
559 |
True |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
560 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
561 |
>>> client.getURLForAlias(alias.id) is None |
562 |
True |
|
4195.1.1
by Brad Crittenden
Implement upload and management of files associated with a product release. |
563 |
|
564 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
565 |
Default View |
566 |
------------ |
|
4989.3.2
by Francis J. Lacoste
Convert moin header. |
567 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
568 |
A librarian file has a default view that should redirect to the download |
569 |
URL. |
|
4195.1.1
by Brad Crittenden
Implement upload and management of files associated with a product release. |
570 |
|
4195.1.12
by Brad Crittenden
Post-review changes |
571 |
>>> from zope.component import getMultiAdapter |
14600.2.2
by Curtis Hovey
Moved webapp to lp.services. |
572 |
>>> from lp.services.webapp.servers import LaunchpadTestRequest |
4195.1.12
by Brad Crittenden
Post-review changes |
573 |
>>> req = LaunchpadTestRequest() |
574 |
>>> alias = lfas.create( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
575 |
... 'text2.txt', len(data), StringIO(data), 'text/plain', |
576 |
... NEVER_EXPIRES) |
|
4195.1.12
by Brad Crittenden
Post-review changes |
577 |
>>> transaction.commit() |
12670.2.13
by William Grant
Fix tests. |
578 |
>>> lfa_view = getMultiAdapter((alias, req), name='+index') |
4195.1.12
by Brad Crittenden
Post-review changes |
579 |
>>> lfa_view.initialize() |
12670.2.13
by William Grant
Fix tests. |
580 |
>>> req.response.getHeader("Location") == alias.getURL() |
4195.1.12
by Brad Crittenden
Post-review changes |
581 |
True |
7077.2.12
by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView. |
582 |
|
583 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
584 |
File views setup |
585 |
---------------- |
|
7077.2.12
by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView. |
586 |
|
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
587 |
We need some files to test different ways of accessing them. |
7077.2.12
by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView. |
588 |
|
589 |
>>> filename = 'public.txt' |
|
590 |
>>> content = 'PUBLIC' |
|
591 |
>>> public_file = getUtility(ILibraryFileAliasSet).create( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
592 |
... filename, len(content), StringIO(content), 'text/plain', |
593 |
... NEVER_EXPIRES, restricted=False) |
|
7077.2.12
by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView. |
594 |
|
595 |
>>> filename = 'restricted.txt' |
|
596 |
>>> content = 'RESTRICTED' |
|
597 |
>>> restricted_file = getUtility(ILibraryFileAliasSet).create( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
598 |
... filename, len(content), StringIO(content), 'text/plain', |
599 |
... NEVER_EXPIRES, restricted=True) |
|
7077.2.12
by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView. |
600 |
|
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
601 |
# Create a new LibraryFileAlias not referencing any LibraryFileContent |
602 |
# record. Such records are considered as being deleted. |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
603 |
|
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
604 |
>>> from lp.services.librarian.model import LibraryFileAlias |
14600.2.2
by Curtis Hovey
Moved webapp to lp.services. |
605 |
>>> from lp.services.webapp.interfaces import ( |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
606 |
... IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
607 |
|
608 |
>>> store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
|
609 |
>>> deleted_file = LibraryFileAlias( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
610 |
... content=None, filename='deleted.txt', mimetype='text/plain') |
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
611 |
>>> ignore = store.add(deleted_file) |
612 |
||
7077.2.16
by Celso Providelo
applying review comments, r=bac. |
613 |
Commit the just-created files. |
7077.2.12
by Celso Providelo
testing StreamOrRedirectLibraryFileAliasView. |
614 |
|
615 |
>>> from canonical.database.sqlbase import commit |
|
616 |
>>> commit() |
|
617 |
||
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
618 |
>>> deleted_file = getUtility(ILibraryFileAliasSet)[deleted_file.id] |
619 |
>>> print deleted_file.deleted |
|
620 |
True |
|
621 |
||
7675.809.30
by Robert Collins
Rename url to path in TimeLimitedToken. |
622 |
Clear out existing tokens. |
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
623 |
|
12393.27.5
by Danilo Segan
Merge with latest accordionoverlay. |
624 |
>>> _ = session_store().find(TimeLimitedToken).remove() |
7675.809.9
by Robert Collins
Implement a RedirectPerhapsWithTokenLibraryFileAliasView which will be the replacement view to use once the librarian server honours tokens. |
625 |
|
7675.809.24
by Robert Collins
Make the public restricted librarian facility be controlled by a feature flag in the appservers. |
626 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
627 |
LibraryFileAliasMD5View |
628 |
----------------------- |
|
8520.4.2
by Curtis Hovey
Added the file name to the md5 file. |
629 |
|
630 |
The MD5 summary for a file can be downloaded. The text file contains the |
|
631 |
hash and file name. |
|
632 |
||
633 |
>>> view = create_view(public_file, '+md5') |
|
634 |
>>> print view.render() |
|
635 |
cd0c6092d6a6874f379fe4827ed1db8b public.txt |
|
636 |
||
637 |
>>> print view.request.response.getHeader('Content-type') |
|
638 |
text/plain |
|
639 |
||
640 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
641 |
Download counts |
642 |
--------------- |
|
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
643 |
|
644 |
The download counts for librarian files are stored in the |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
645 |
LibraryFileDownloadCount table, broken down by day and country, but |
646 |
there's also a 'hits' attribute on ILibraryFileAlias, which holds the |
|
647 |
total number of times that file has been downloaded. |
|
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
648 |
|
7675.86.5
by Guilherme Salgado
Fix a typo |
649 |
The count starts at 0, and cannot be changed directly. |
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
650 |
|
651 |
>>> public_file.hits |
|
652 |
0 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
653 |
|
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
654 |
>>> public_file.hits = 10 |
655 |
Traceback (most recent call last): |
|
656 |
... |
|
657 |
ForbiddenAttribute: ... |
|
658 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
659 |
To change that, we have to use the updateDownloadCount() method, which |
660 |
takes care of creating/updating the necessary LibraryFileDownloadCount |
|
661 |
entries. |
|
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
662 |
|
8356.1.13
by Leonard Richardson
Moved in Spokenin and Country. |
663 |
>>> from lp.services.worlddata.interfaces.country import ICountrySet |
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
664 |
>>> country_set = getUtility(ICountrySet) |
8499.2.4
by Guilherme Salgado
A couple improvements as suggested by Martin |
665 |
>>> november_1st_2006 = date(2006, 11, 1) |
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
666 |
>>> brazil = country_set['BR'] |
667 |
>>> public_file.updateDownloadCount(november_1st_2006, brazil, count=1) |
|
668 |
>>> public_file.hits |
|
669 |
1 |
|
670 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
671 |
This was the first hit for that file from Brazil on 2006 November first, |
672 |
so a new LibraryFileDownloadCount was created. |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
673 |
|
14578.2.1
by William Grant
Move librarian stuff from canonical.launchpad to lp.services.librarian. canonical.librarian remains untouched. |
674 |
>>> from lp.services.librarian.model import ( |
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
675 |
... LibraryFileDownloadCount) |
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
676 |
>>> from storm.locals import Store |
677 |
>>> store = Store.of(public_file) |
|
678 |
>>> brazil_entry = store.find( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
679 |
... LibraryFileDownloadCount, libraryfilealias=public_file, |
680 |
... country=brazil, day=november_1st_2006).one() |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
681 |
>>> brazil_entry.count |
682 |
1 |
|
683 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
684 |
Below we simulate a hit from Japan on that same day, which will also |
685 |
create a new LibraryFileDownloadCount. |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
686 |
|
7675.86.3
by Guilherme Salgado
Expose ILibraryFileAlias.hits and implement its updateDownloadCount() method. |
687 |
>>> japan = country_set['JP'] |
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
688 |
>>> public_file.updateDownloadCount(november_1st_2006, japan, count=3) |
689 |
>>> public_file.hits |
|
690 |
4 |
|
691 |
||
692 |
>>> japan_entry = store.find( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
693 |
... LibraryFileDownloadCount, libraryfilealias=public_file, |
694 |
... country=japan, day=november_1st_2006).one() |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
695 |
>>> japan_entry.count |
696 |
3 |
|
697 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
698 |
If there's another hit from Brazil on the same day, the existing entry |
699 |
will be updated. |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
700 |
|
701 |
>>> public_file.updateDownloadCount(november_1st_2006, brazil, count=2) |
|
702 |
>>> public_file.hits |
|
703 |
6 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
704 |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
705 |
>>> brazil_entry.count |
706 |
3 |
|
707 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
708 |
If the hit happened on a different day, a separate entry would be |
709 |
created. |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
710 |
|
8499.2.4
by Guilherme Salgado
A couple improvements as suggested by Martin |
711 |
>>> november_2nd_2006 = date(2006, 11, 2) |
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
712 |
>>> public_file.updateDownloadCount(november_2nd_2006, brazil, count=10) |
713 |
>>> public_file.hits |
|
714 |
16 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
715 |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
716 |
>>> brazil_entry2 = store.find( |
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
717 |
... LibraryFileDownloadCount, libraryfilealias=public_file, |
718 |
... country=brazil, day=november_2nd_2006).one() |
|
7675.86.7
by Guilherme Salgado
A few more tests, as requested by Danilo |
719 |
>>> brazil_entry2.count |
720 |
10 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
721 |
|
8499.2.5
by Guilherme Salgado
A few changes suggested by Michael |
722 |
>>> last_downloaded_date = november_2nd_2006 |
8499.2.2
by Guilherme Salgado
Show download counts on the productrelease +index page |
723 |
|
724 |
||
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
725 |
Time to last download |
726 |
--------------------- |
|
8499.2.4
by Guilherme Salgado
A couple improvements as suggested by Martin |
727 |
|
13980.2.1
by Jeroen Vermeulen
Lint. Lots of lint. |
728 |
The .last_downloaded property gives us the time delta from today to the |
729 |
day that file was last downloaded, or None if it's never been |
|
730 |
downloaded. |
|
8499.2.4
by Guilherme Salgado
A couple improvements as suggested by Martin |
731 |
|
732 |
>>> today = datetime.now(utc).date() |
|
8499.2.5
by Guilherme Salgado
A few changes suggested by Michael |
733 |
>>> public_file.last_downloaded == today - last_downloaded_date |
8499.2.4
by Guilherme Salgado
A couple improvements as suggested by Martin |
734 |
True |
8499.2.2
by Guilherme Salgado
Show download counts on the productrelease +index page |
735 |
|
736 |
>>> content = 'something' |
|
737 |
>>> brand_new_file = getUtility(ILibraryFileAliasSet).create( |
|
13980.2.5
by Jeroen Vermeulen
Fixed up bad doctest indentaiton introduced by formatdoctest. Thanks Benji for taking on this review. |
738 |
... 'new.txt', len(content), StringIO(content), 'text/plain', |
739 |
... NEVER_EXPIRES, restricted=False) |
|
8499.2.2
by Guilherme Salgado
Show download counts on the productrelease +index page |
740 |
>>> print brand_new_file.last_downloaded |
741 |
None |