~launchpad-pqm/launchpad/devel

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