10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
1 |
# Copyright 2010 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
3 |
||
4 |
# We like global statements!
|
|
5 |
# pylint: disable-msg=W0602,W0603
|
|
6 |
__metaclass__ = type |
|
7 |
||
8 |
__all__ = [ |
|
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
9 |
'launchpadlib_credentials_for', |
10 |
'launchpadlib_for', |
|
10714.2.3
by Brad Crittenden
Further convert project launchpadlib test. |
11 |
'oauth_access_token_for', |
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
12 |
]
|
13 |
||
10747.1.1
by Aaron Bentley
Export createRecipe to web service. |
14 |
|
11091.8.1
by Benji York
make the tests use a fresh cache directory for each Launchpad object |
15 |
import shutil |
16 |
import tempfile |
|
11579.1.1
by Leonard Richardson
Reverted my earlier change. |
17 |
|
18 |
from launchpadlib.credentials import ( |
|
19 |
AccessToken, |
|
11998.1.1
by Guilherme Salgado
Add a launchpad.View security adapter for ISpecification so that collections of it can be seen anonymously on the webservice |
20 |
AnonymousAccessToken, |
11579.1.1
by Leonard Richardson
Reverted my earlier change. |
21 |
Credentials, |
22 |
)
|
|
23 |
from launchpadlib.launchpad import Launchpad |
|
10747.1.1
by Aaron Bentley
Export createRecipe to web service. |
24 |
import transaction |
11277.3.1
by Robert Collins
Extract QueryCounter from pofile doctest for reuse. |
25 |
from zope.app.publication.interfaces import IEndRequestEvent |
26 |
from zope.app.testing import ztapi |
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
27 |
from zope.component import getUtility |
11091.8.3
by Benji York
tweak implementation after MP feedback to use zope.testing.cleanup to |
28 |
import zope.testing.cleanup |
11277.3.1
by Robert Collins
Extract QueryCounter from pofile doctest for reuse. |
29 |
|
11787.1.4
by Curtis Hovey
Deglobbed testing. |
30 |
from canonical.launchpad.interfaces.oauth import IOAuthConsumerSet |
11277.3.1
by Robert Collins
Extract QueryCounter from pofile doctest for reuse. |
31 |
from canonical.launchpad.webapp.adapter import get_request_statements |
10721.2.3
by Francis J. Lacoste
Import ANONYMOUS directly from canonical.launchpad.webapp.interaction. |
32 |
from canonical.launchpad.webapp.interaction import ANONYMOUS |
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
33 |
from canonical.launchpad.webapp.interfaces import OAuthPermission |
12499.1.1
by Leonard Richardson
Initial implementation. |
34 |
from canonical.launchpad.webapp.publisher import canonical_url |
11787.1.4
by Curtis Hovey
Deglobbed testing. |
35 |
from lp.registry.interfaces.person import IPersonSet |
11579.1.1
by Leonard Richardson
Reverted my earlier change. |
36 |
from lp.testing._login import ( |
37 |
login, |
|
38 |
logout, |
|
39 |
)
|
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
40 |
|
10714.2.3
by Brad Crittenden
Further convert project launchpadlib test. |
41 |
|
12499.1.1
by Leonard Richardson
Initial implementation. |
42 |
def api_url(obj): |
43 |
"""Find the web service URL of a data model object.
|
|
44 |
||
45 |
This makes it easy to load up the factory object you just created
|
|
46 |
in launchpadlib.
|
|
47 |
||
48 |
:param: Which web service version to use.
|
|
49 |
||
50 |
:return: A relative URL suitable for passing into Launchpad.load().
|
|
51 |
"""
|
|
52 |
return canonical_url(obj, force_local_path=True) |
|
53 |
||
54 |
||
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
55 |
def oauth_access_token_for(consumer_name, person, permission, context=None): |
56 |
"""Find or create an OAuth access token for the given person.
|
|
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
57 |
:param consumer_name: An OAuth consumer name.
|
58 |
:param person: A person (or the name of a person) for whom to create
|
|
59 |
or find credentials.
|
|
60 |
:param permission: An OAuthPermission (or its token) designating
|
|
61 |
the level of permission the credentials should have.
|
|
62 |
:param context: The OAuth context for the credentials (or a string
|
|
63 |
designating same).
|
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
64 |
|
65 |
:return: An OAuthAccessToken object.
|
|
66 |
"""
|
|
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
67 |
if isinstance(person, basestring): |
68 |
# Look up a person by name.
|
|
69 |
person = getUtility(IPersonSet).getByName(person) |
|
70 |
if isinstance(context, basestring): |
|
71 |
# Turn an OAuth context string into the corresponding object.
|
|
72 |
# Avoid an import loop by importing from launchpad.browser here.
|
|
73 |
from canonical.launchpad.browser.oauth import lookup_oauth_context |
|
74 |
context = lookup_oauth_context(context) |
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
75 |
if isinstance(permission, basestring): |
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
76 |
# Look up a permission by its token string.
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
77 |
permission = OAuthPermission.items[permission] |
78 |
||
79 |
# Find or create the consumer object.
|
|
80 |
consumer_set = getUtility(IOAuthConsumerSet) |
|
81 |
consumer = consumer_set.getByKey(consumer_name) |
|
82 |
if consumer is None: |
|
83 |
consumer = consumer_set.new(consumer_name) |
|
84 |
else: |
|
85 |
# We didn't have to create the consumer. Maybe this user
|
|
86 |
# already has an access token for this
|
|
87 |
# consumer+person+permission?
|
|
88 |
existing_token = [token for token in person.oauth_access_tokens |
|
89 |
if (token.consumer == consumer |
|
90 |
and token.permission == permission |
|
91 |
and token.context == context)] |
|
92 |
if len(existing_token) >= 1: |
|
93 |
return existing_token[0] |
|
94 |
||
95 |
# There is no existing access token for this
|
|
96 |
# consumer+person+permission+context. Create one and review it.
|
|
97 |
request_token = consumer.newRequestToken() |
|
98 |
request_token.review(person, permission, context) |
|
99 |
access_token = request_token.createAccessToken() |
|
100 |
return access_token |
|
101 |
||
102 |
||
103 |
def launchpadlib_credentials_for( |
|
104 |
consumer_name, person, permission=OAuthPermission.WRITE_PRIVATE, |
|
105 |
context=None): |
|
106 |
"""Create launchpadlib credentials for the given person.
|
|
107 |
||
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
108 |
:param consumer_name: An OAuth consumer name.
|
109 |
:param person: A person (or the name of a person) for whom to create
|
|
110 |
or find credentials.
|
|
111 |
:param permission: An OAuthPermission (or its token) designating
|
|
112 |
the level of permission the credentials should have.
|
|
113 |
:param context: The OAuth context for the credentials.
|
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
114 |
:return: A launchpadlib Credentials object.
|
115 |
"""
|
|
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
116 |
# Start an interaction so that oauth_access_token_for will
|
117 |
# succeed. oauth_access_token_for may be called in any layer, but
|
|
118 |
# launchpadlib_credentials_for is only called in the
|
|
119 |
# PageTestLayer, when a Launchpad instance is running for
|
|
120 |
# launchpadlib to use.
|
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
121 |
login(ANONYMOUS) |
122 |
access_token = oauth_access_token_for( |
|
123 |
consumer_name, person, permission, context) |
|
124 |
logout() |
|
125 |
launchpadlib_token = AccessToken( |
|
126 |
access_token.key, access_token.secret) |
|
127 |
return Credentials(consumer_name=consumer_name, |
|
128 |
access_token=launchpadlib_token) |
|
129 |
||
11091.8.4
by Benji York
- add docstring |
130 |
|
11091.8.3
by Benji York
tweak implementation after MP feedback to use zope.testing.cleanup to |
131 |
def _clean_up_cache(cache): |
12113.1.4
by Abel Deuring
new webservice test class TestLaunchpad, providing a method to access the raw response to a webservice request. |
132 |
"""Clean up a temporary launchpadlib cache directory."""
|
11091.8.3
by Benji York
tweak implementation after MP feedback to use zope.testing.cleanup to |
133 |
shutil.rmtree(cache, ignore_errors=True) |
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
134 |
|
11091.8.4
by Benji York
- add docstring |
135 |
|
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
136 |
def launchpadlib_for( |
12095.2.1
by j.c.sackett
Removed launchpadlib_for_anonymous by extending launchpadlib_for. |
137 |
consumer_name, person=None, permission=OAuthPermission.WRITE_PRIVATE, |
12499.1.1
by Leonard Richardson
Initial implementation. |
138 |
context=None, version="devel", service_root="http://api.launchpad.dev/"): |
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
139 |
"""Create a Launchpad object for the given person.
|
140 |
||
10604.7.3
by Leonard Richardson
Refactored and added API doc. |
141 |
:param consumer_name: An OAuth consumer name.
|
142 |
:param person: A person (or the name of a person) for whom to create
|
|
143 |
or find credentials.
|
|
144 |
:param permission: An OAuthPermission (or its token) designating
|
|
145 |
the level of permission the credentials should have.
|
|
146 |
:param context: The OAuth context for the credentials.
|
|
147 |
:param version: The version of the web service to access.
|
|
148 |
:param service_root: The root URL of the web service to access.
|
|
149 |
||
10604.7.2
by Leonard Richardson
Initial implementation of helper methods. |
150 |
:return: A launchpadlib Launchpad object.
|
151 |
"""
|
|
12095.2.1
by j.c.sackett
Removed launchpadlib_for_anonymous by extending launchpadlib_for. |
152 |
if person is None: |
153 |
token = AnonymousAccessToken() |
|
154 |
credentials = Credentials(consumer_name, access_token=token) |
|
155 |
else: |
|
156 |
credentials = launchpadlib_credentials_for( |
|
157 |
consumer_name, person, permission, context) |
|
10747.1.1
by Aaron Bentley
Export createRecipe to web service. |
158 |
transaction.commit() |
11091.8.1
by Benji York
make the tests use a fresh cache directory for each Launchpad object |
159 |
cache = tempfile.mkdtemp(prefix='launchpadlib-cache-') |
11091.8.3
by Benji York
tweak implementation after MP feedback to use zope.testing.cleanup to |
160 |
zope.testing.cleanup.addCleanUp(_clean_up_cache, (cache,)) |
12113.1.7
by Abel Deuring
devel merged |
161 |
return Launchpad(credentials, None, None, service_root=service_root, |
162 |
version=version, cache=cache) |
|
11277.3.1
by Robert Collins
Extract QueryCounter from pofile doctest for reuse. |
163 |
|
164 |
||
165 |
class QueryCollector: |
|
166 |
"""Collect database calls made in web requests.
|
|
167 |
||
168 |
These are only retrievable at the end of a request, and for tests it is
|
|
12278.1.1
by Graham Binns
Reverted the reversion of devel r12272. |
169 |
useful to be able to make assertions about the calls made during a
|
12113.1.4
by Abel Deuring
new webservice test class TestLaunchpad, providing a method to access the raw response to a webservice request. |
170 |
request: this class provides a tool to gather them in a simple fashion.
|
11277.3.1
by Robert Collins
Extract QueryCounter from pofile doctest for reuse. |
171 |
|
172 |
:ivar count: The count of db queries the last web request made.
|
|
173 |
:ivar queries: The list of queries made. See
|
|
174 |
canonical.launchpad.webapp.adapter.get_request_statements for more
|
|
175 |
information.
|
|
176 |
"""
|
|
177 |
||
178 |
def __init__(self): |
|
179 |
self._active = False |
|
180 |
self.count = None |
|
181 |
self.queries = None |
|
182 |
||
183 |
def register(self): |
|
184 |
"""Start counting queries.
|
|
7675.769.1
by Jonathan Lange
Merge stable, resolving conflicts. |
185 |
|
11277.3.1
by Robert Collins
Extract QueryCounter from pofile doctest for reuse. |
186 |
Be sure to call unregister when finished with the collector.
|
187 |
||
188 |
After each web request the count and queries attributes are updated.
|
|
189 |
"""
|
|
190 |
ztapi.subscribe((IEndRequestEvent, ), None, self) |
|
191 |
self._active = True |
|
192 |
||
193 |
def __call__(self, event): |
|
194 |
if self._active: |
|
195 |
self.queries = get_request_statements() |
|
196 |
self.count = len(self.queries) |
|
197 |
||
198 |
def unregister(self): |
|
199 |
self._active = False |